/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2020 - Raw Material Software Limited

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).

   End User License Agreement: www.juce.com/juce-6-licence
   Privacy Policy: www.juce.com/juce-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

#include "juce_PatchedMP3AudioFormat.h"

namespace juce {

/*
    IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag
   and to compile this MP3 code into your software, you do so AT YOUR OWN RISK!
   By doing so, you are agreeing that Raw Material Software Limited is in no way
   responsible for any patent, copyright, or other legal issues that you may
   suffer as a result.

    The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from
   infringements of 3rd-party intellectual property. If you wish to use it,
   please seek your own independent advice about the legality of doing so. If
   you are not willing to accept full responsibility for the consequences of
   using this code, then do not enable the JUCE_USE_MP3AUDIOFORMAT setting.
*/
namespace PatchedMP3Decoder {

struct AllocationTable {
  int16 bits, d;
};

constexpr AllocationTable allocTable0[] = {
    {4, 0},       {5, 3},       {3, -3},      {4, -7},      {5, -15},
    {6, -31},     {7, -63},     {8, -127},    {9, -255},    {10, -511},
    {11, -1023},  {12, -2047},  {13, -4095},  {14, -8191},  {15, -16383},
    {16, -32767}, {4, 0},       {5, 3},       {3, -3},      {4, -7},
    {5, -15},     {6, -31},     {7, -63},     {8, -127},    {9, -255},
    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},  {14, -8191},
    {15, -16383}, {16, -32767}, {4, 0},       {5, 3},       {3, -3},
    {4, -7},      {5, -15},     {6, -31},     {7, -63},     {8, -127},
    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},
    {14, -8191},  {15, -16383}, {16, -32767}, {4, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {6, -31},     {7, -63},     {8, -127},    {9, -255},    {10, -511},
    {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767}, {4, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {6, -31},     {7, -63},     {8, -127},    {9, -255},
    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767},
    {4, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},     {7, -63},     {8, -127},
    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},
    {16, -32767}, {4, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {6, -31},     {7, -63},
    {8, -127},    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},
    {13, -4095},  {16, -32767}, {4, 0},       {5, 3},       {7, 5},
    {3, -3},      {10, 9},      {4, -7},      {5, -15},     {6, -31},
    {7, -63},     {8, -127},    {9, -255},    {10, -511},   {11, -1023},
    {12, -2047},  {13, -4095},  {16, -32767}, {4, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {6, -31},     {7, -63},     {8, -127},    {9, -255},    {10, -511},
    {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767}, {4, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {6, -31},     {7, -63},     {8, -127},    {9, -255},
    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767},
    {4, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},     {7, -63},     {8, -127},
    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},
    {16, -32767}, {3, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {16, -32767}, {3, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {16, -32767}, {3, 0},       {5, 3},       {7, 5},
    {3, -3},      {10, 9},      {4, -7},      {5, -15},     {16, -32767},
    {3, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {16, -32767}, {3, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {16, -32767}, {3, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {16, -32767}, {3, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {16, -32767}, {3, 0},       {5, 3},       {7, 5},
    {3, -3},      {10, 9},      {4, -7},      {5, -15},     {16, -32767},
    {3, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {16, -32767}, {3, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {16, -32767}, {3, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {16, -32767}, {3, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {16, -32767}, {2, 0},       {5, 3},       {7, 5},
    {16, -32767}, {2, 0},       {5, 3},       {7, 5},       {16, -32767},
    {2, 0},       {5, 3},       {7, 5},       {16, -32767}, {2, 0},
    {5, 3},       {7, 5},       {16, -32767}};

constexpr AllocationTable allocTable1[] = {
    {4, 0},       {5, 3},       {3, -3},      {4, -7},      {5, -15},
    {6, -31},     {7, -63},     {8, -127},    {9, -255},    {10, -511},
    {11, -1023},  {12, -2047},  {13, -4095},  {14, -8191},  {15, -16383},
    {16, -32767}, {4, 0},       {5, 3},       {3, -3},      {4, -7},
    {5, -15},     {6, -31},     {7, -63},     {8, -127},    {9, -255},
    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},  {14, -8191},
    {15, -16383}, {16, -32767}, {4, 0},       {5, 3},       {3, -3},
    {4, -7},      {5, -15},     {6, -31},     {7, -63},     {8, -127},
    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},
    {14, -8191},  {15, -16383}, {16, -32767}, {4, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {6, -31},     {7, -63},     {8, -127},    {9, -255},    {10, -511},
    {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767}, {4, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {6, -31},     {7, -63},     {8, -127},    {9, -255},
    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767},
    {4, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},     {7, -63},     {8, -127},
    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},
    {16, -32767}, {4, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {6, -31},     {7, -63},
    {8, -127},    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},
    {13, -4095},  {16, -32767}, {4, 0},       {5, 3},       {7, 5},
    {3, -3},      {10, 9},      {4, -7},      {5, -15},     {6, -31},
    {7, -63},     {8, -127},    {9, -255},    {10, -511},   {11, -1023},
    {12, -2047},  {13, -4095},  {16, -32767}, {4, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {6, -31},     {7, -63},     {8, -127},    {9, -255},    {10, -511},
    {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767}, {4, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {6, -31},     {7, -63},     {8, -127},    {9, -255},
    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},  {16, -32767},
    {4, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},     {7, -63},     {8, -127},
    {9, -255},    {10, -511},   {11, -1023},  {12, -2047},  {13, -4095},
    {16, -32767}, {3, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {16, -32767}, {3, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {16, -32767}, {3, 0},       {5, 3},       {7, 5},
    {3, -3},      {10, 9},      {4, -7},      {5, -15},     {16, -32767},
    {3, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {16, -32767}, {3, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {16, -32767}, {3, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {16, -32767}, {3, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {16, -32767}, {3, 0},       {5, 3},       {7, 5},
    {3, -3},      {10, 9},      {4, -7},      {5, -15},     {16, -32767},
    {3, 0},       {5, 3},       {7, 5},       {3, -3},      {10, 9},
    {4, -7},      {5, -15},     {16, -32767}, {3, 0},       {5, 3},
    {7, 5},       {3, -3},      {10, 9},      {4, -7},      {5, -15},
    {16, -32767}, {3, 0},       {5, 3},       {7, 5},       {3, -3},
    {10, 9},      {4, -7},      {5, -15},     {16, -32767}, {3, 0},
    {5, 3},       {7, 5},       {3, -3},      {10, 9},      {4, -7},
    {5, -15},     {16, -32767}, {2, 0},       {5, 3},       {7, 5},
    {16, -32767}, {2, 0},       {5, 3},       {7, 5},       {16, -32767},
    {2, 0},       {5, 3},       {7, 5},       {16, -32767}, {2, 0},
    {5, 3},       {7, 5},       {16, -32767}, {2, 0},       {5, 3},
    {7, 5},       {16, -32767}, {2, 0},       {5, 3},       {7, 5},
    {16, -32767}, {2, 0},       {5, 3},       {7, 5},       {16, -32767}};

constexpr AllocationTable allocTable2[] = {
    {4, 0},       {5, 3},       {7, 5},      {10, 9},     {4, -7},
    {5, -15},     {6, -31},     {7, -63},    {8, -127},   {9, -255},
    {10, -511},   {11, -1023},  {12, -2047}, {13, -4095}, {14, -8191},
    {15, -16383}, {4, 0},       {5, 3},      {7, 5},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},    {7, -63},    {8, -127},
    {9, -255},    {10, -511},   {11, -1023}, {12, -2047}, {13, -4095},
    {14, -8191},  {15, -16383}, {3, 0},      {5, 3},      {7, 5},
    {10, 9},      {4, -7},      {5, -15},    {6, -31},    {7, -63},
    {3, 0},       {5, 3},       {7, 5},      {10, 9},     {4, -7},
    {5, -15},     {6, -31},     {7, -63},    {3, 0},      {5, 3},
    {7, 5},       {10, 9},      {4, -7},     {5, -15},    {6, -31},
    {7, -63},     {3, 0},       {5, 3},      {7, 5},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},    {7, -63},    {3, 0},
    {5, 3},       {7, 5},       {10, 9},     {4, -7},     {5, -15},
    {6, -31},     {7, -63},     {3, 0},      {5, 3},      {7, 5},
    {10, 9},      {4, -7},      {5, -15},    {6, -31},    {7, -63}};

constexpr AllocationTable allocTable3[] = {
    {4, 0},       {5, 3},       {7, 5},      {10, 9},     {4, -7},
    {5, -15},     {6, -31},     {7, -63},    {8, -127},   {9, -255},
    {10, -511},   {11, -1023},  {12, -2047}, {13, -4095}, {14, -8191},
    {15, -16383}, {4, 0},       {5, 3},      {7, 5},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},    {7, -63},    {8, -127},
    {9, -255},    {10, -511},   {11, -1023}, {12, -2047}, {13, -4095},
    {14, -8191},  {15, -16383}, {3, 0},      {5, 3},      {7, 5},
    {10, 9},      {4, -7},      {5, -15},    {6, -31},    {7, -63},
    {3, 0},       {5, 3},       {7, 5},      {10, 9},     {4, -7},
    {5, -15},     {6, -31},     {7, -63},    {3, 0},      {5, 3},
    {7, 5},       {10, 9},      {4, -7},     {5, -15},    {6, -31},
    {7, -63},     {3, 0},       {5, 3},      {7, 5},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},    {7, -63},    {3, 0},
    {5, 3},       {7, 5},       {10, 9},     {4, -7},     {5, -15},
    {6, -31},     {7, -63},     {3, 0},      {5, 3},      {7, 5},
    {10, 9},      {4, -7},      {5, -15},    {6, -31},    {7, -63},
    {3, 0},       {5, 3},       {7, 5},      {10, 9},     {4, -7},
    {5, -15},     {6, -31},     {7, -63},    {3, 0},      {5, 3},
    {7, 5},       {10, 9},      {4, -7},     {5, -15},    {6, -31},
    {7, -63},     {3, 0},       {5, 3},      {7, 5},      {10, 9},
    {4, -7},      {5, -15},     {6, -31},    {7, -63},    {3, 0},
    {5, 3},       {7, 5},       {10, 9},     {4, -7},     {5, -15},
    {6, -31},     {7, -63}};

constexpr AllocationTable allocTable4[] = {
    {4, 0},      {5, 3},      {7, 5},      {3, -3},     {10, 9},
    {4, -7},     {5, -15},    {6, -31},    {7, -63},    {8, -127},
    {9, -255},   {10, -511},  {11, -1023}, {12, -2047}, {13, -4095},
    {14, -8191}, {4, 0},      {5, 3},      {7, 5},      {3, -3},
    {10, 9},     {4, -7},     {5, -15},    {6, -31},    {7, -63},
    {8, -127},   {9, -255},   {10, -511},  {11, -1023}, {12, -2047},
    {13, -4095}, {14, -8191}, {4, 0},      {5, 3},      {7, 5},
    {3, -3},     {10, 9},     {4, -7},     {5, -15},    {6, -31},
    {7, -63},    {8, -127},   {9, -255},   {10, -511},  {11, -1023},
    {12, -2047}, {13, -4095}, {14, -8191}, {4, 0},      {5, 3},
    {7, 5},      {3, -3},     {10, 9},     {4, -7},     {5, -15},
    {6, -31},    {7, -63},    {8, -127},   {9, -255},   {10, -511},
    {11, -1023}, {12, -2047}, {13, -4095}, {14, -8191}, {3, 0},
    {5, 3},      {7, 5},      {10, 9},     {4, -7},     {5, -15},
    {6, -31},    {7, -63},    {3, 0},      {5, 3},      {7, 5},
    {10, 9},     {4, -7},     {5, -15},    {6, -31},    {7, -63},
    {3, 0},      {5, 3},      {7, 5},      {10, 9},     {4, -7},
    {5, -15},    {6, -31},    {7, -63},    {3, 0},      {5, 3},
    {7, 5},      {10, 9},     {4, -7},     {5, -15},    {6, -31},
    {7, -63},    {3, 0},      {5, 3},      {7, 5},      {10, 9},
    {4, -7},     {5, -15},    {6, -31},    {7, -63},    {3, 0},
    {5, 3},      {7, 5},      {10, 9},     {4, -7},     {5, -15},
    {6, -31},    {7, -63},    {3, 0},      {5, 3},      {7, 5},
    {10, 9},     {4, -7},     {5, -15},    {6, -31},    {7, -63},
    {2, 0},      {5, 3},      {7, 5},      {10, 9},     {2, 0},
    {5, 3},      {7, 5},      {10, 9},     {2, 0},      {5, 3},
    {7, 5},      {10, 9},     {2, 0},      {5, 3},      {7, 5},
    {10, 9},     {2, 0},      {5, 3},      {7, 5},      {10, 9},
    {2, 0},      {5, 3},      {7, 5},      {10, 9},     {2, 0},
    {5, 3},      {7, 5},      {10, 9},     {2, 0},      {5, 3},
    {7, 5},      {10, 9},     {2, 0},      {5, 3},      {7, 5},
    {10, 9},     {2, 0},      {5, 3},      {7, 5},      {10, 9},
    {2, 0},      {5, 3},      {7, 5},      {10, 9},     {2, 0},
    {5, 3},      {7, 5},      {10, 9},     {2, 0},      {5, 3},
    {7, 5},      {10, 9},     {2, 0},      {5, 3},      {7, 5},
    {10, 9},     {2, 0},      {5, 3},      {7, 5},      {10, 9},
    {2, 0},      {5, 3},      {7, 5},      {10, 9},     {2, 0},
    {5, 3},      {7, 5},      {10, 9},     {2, 0},      {5, 3},
    {7, 5},      {10, 9},     {2, 0},      {5, 3},      {7, 5},
    {10, 9}};

struct BandInfoStruct {
  int16 longIndex[23];
  int16 longDiff[22];
  int16 shortIndex[14];
  int16 shortDiff[13];
};

constexpr BandInfoStruct bandInfo[9] = {
    {{0,  4,  8,   12,  16,  20,  24,  30,  36,  44,  52, 62,
      74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576},
     {4,  4,  4,  4,  4,  4,  6,  6,  8,  8,  10,
      12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158},
     {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 40 * 3, 52 * 3, 66 * 3,
      84 * 3, 106 * 3, 136 * 3, 192 * 3},
     {4, 4, 4, 4, 6, 8, 10, 12, 14, 18, 22, 30, 56}},

    {{0,  4,  8,   12,  16,  20,  24,  30,  36,  42,  50, 60,
      72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576},
     {4,  4,  4,  4,  4,  4,  6,  6,  6,  8,  10,
      12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192},
     {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 28 * 3, 38 * 3, 50 * 3, 64 * 3,
      80 * 3, 100 * 3, 126 * 3, 192 * 3},
     {4, 4, 4, 4, 6, 6, 10, 12, 14, 16, 20, 26, 66}},

    {{0,  4,   8,   12,  16,  20,  24,  30,  36,  44,  54, 66,
      82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576},
     {4,  4,  4,  4,  4,  4,  6,  6,  8,  10,  12,
      16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26},
     {0, 4 * 3, 8 * 3, 12 * 3, 16 * 3, 22 * 3, 30 * 3, 42 * 3, 58 * 3, 78 * 3,
      104 * 3, 138 * 3, 180 * 3, 192 * 3},
     {4, 4, 4, 4, 6, 8, 12, 16, 20, 26, 34, 42, 12}},

    {{0,   6,   12,  18,  24,  30,  36,  44,  54,  66,  80, 96,
      116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576},
     {6,  6,  6,  6,  6,  6,  8,  10, 12, 14, 16,
      20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54},
     {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 24 * 3, 32 * 3, 42 * 3, 56 * 3, 74 * 3,
      100 * 3, 132 * 3, 174 * 3, 192 * 3},
     {4, 4, 4, 6, 6, 8, 10, 14, 18, 26, 32, 42, 18}},

    {{0,   6,   12,  18,  24,  30,  36,  44,  54,  66,  80, 96,
      114, 136, 162, 194, 232, 278, 332, 394, 464, 540, 576},
     {6,  6,  6,  6,  6,  6,  8,  10, 12, 14, 16,
      18, 22, 26, 32, 38, 46, 54, 62, 70, 76, 36},
     {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3,
      104 * 3, 136 * 3, 180 * 3, 192 * 3},
     {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 32, 44, 12}},

    {{0,   6,   12,  18,  24,  30,  36,  44,  54,  66,  80, 96,
      116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576},
     {6,  6,  6,  6,  6,  6,  8,  10, 12, 14, 16,
      20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54},
     {0, 4 * 3, 8 * 3, 12 * 3, 18 * 3, 26 * 3, 36 * 3, 48 * 3, 62 * 3, 80 * 3,
      104 * 3, 134 * 3, 174 * 3, 192 * 3},
     {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}},

    {{0,   6,   12,  18,  24,  30,  36,  44,  54,  66,  80, 96,
      116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576},
     {6,  6,  6,  6,  6,  6,  8,  10, 12, 14, 16,
      20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54},
     {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576},
     {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}},

    {{0,   6,   12,  18,  24,  30,  36,  44,  54,  66,  80, 96,
      116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576},
     {6,  6,  6,  6,  6,  6,  8,  10, 12, 14, 16,
      20, 24, 28, 32, 38, 46, 52, 60, 68, 58, 54},
     {0, 12, 24, 36, 54, 78, 108, 144, 186, 240, 312, 402, 522, 576},
     {4, 4, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40, 18}},

    {{0,   12,  24,  36,  48,  60,  72,  88,  108, 132, 160, 192,
      232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576},
     {12, 12, 12, 12, 12, 12, 16, 20, 24, 28, 32,
      40, 48, 56, 64, 76, 90, 2,  2,  2,  2,  2},
     {0, 24, 48, 72, 108, 156, 216, 288, 372, 480, 486, 492, 498, 576},
     {8, 8, 8, 12, 16, 20, 24, 28, 36, 2, 2, 2, 26}}};

constexpr double decodeWindow[] = {
    0.000000000,  -0.000015259, -0.000015259, -0.000015259, -0.000015259,
    -0.000015259, -0.000015259, -0.000030518, -0.000030518, -0.000030518,
    -0.000030518, -0.000045776, -0.000045776, -0.000061035, -0.000061035,
    -0.000076294, -0.000076294, -0.000091553, -0.000106812, -0.000106812,
    -0.000122070, -0.000137329, -0.000152588, -0.000167847, -0.000198364,
    -0.000213623, -0.000244141, -0.000259399, -0.000289917, -0.000320435,
    -0.000366211, -0.000396729, -0.000442505, -0.000473022, -0.000534058,
    -0.000579834, -0.000625610, -0.000686646, -0.000747681, -0.000808716,
    -0.000885010, -0.000961304, -0.001037598, -0.001113892, -0.001205444,
    -0.001296997, -0.001388550, -0.001480103, -0.001586914, -0.001693726,
    -0.001785278, -0.001907349, -0.002014160, -0.002120972, -0.002243042,
    -0.002349854, -0.002456665, -0.002578735, -0.002685547, -0.002792358,
    -0.002899170, -0.002990723, -0.003082275, -0.003173828, -0.003250122,
    -0.003326416, -0.003387451, -0.003433228, -0.003463745, -0.003479004,
    -0.003479004, -0.003463745, -0.003417969, -0.003372192, -0.003280640,
    -0.003173828, -0.003051758, -0.002883911, -0.002700806, -0.002487183,
    -0.002227783, -0.001937866, -0.001617432, -0.001266479, -0.000869751,
    -0.000442505, 0.000030518,  0.000549316,  0.001098633,  0.001693726,
    0.002334595,  0.003005981,  0.003723145,  0.004486084,  0.005294800,
    0.006118774,  0.007003784,  0.007919312,  0.008865356,  0.009841919,
    0.010848999,  0.011886597,  0.012939453,  0.014022827,  0.015121460,
    0.016235352,  0.017349243,  0.018463135,  0.019577026,  0.020690918,
    0.021789551,  0.022857666,  0.023910522,  0.024932861,  0.025909424,
    0.026840210,  0.027725220,  0.028533936,  0.029281616,  0.029937744,
    0.030532837,  0.031005859,  0.031387329,  0.031661987,  0.031814575,
    0.031845093,  0.031738281,  0.031478882,  0.031082153,  0.030517578,
    0.029785156,  0.028884888,  0.027801514,  0.026535034,  0.025085449,
    0.023422241,  0.021575928,  0.019531250,  0.017257690,  0.014801025,
    0.012115479,  0.009231567,  0.006134033,  0.002822876,  -0.000686646,
    -0.004394531, -0.008316040, -0.012420654, -0.016708374, -0.021179199,
    -0.025817871, -0.030609131, -0.035552979, -0.040634155, -0.045837402,
    -0.051132202, -0.056533813, -0.061996460, -0.067520142, -0.073059082,
    -0.078628540, -0.084182739, -0.089706421, -0.095169067, -0.100540161,
    -0.105819702, -0.110946655, -0.115921021, -0.120697021, -0.125259399,
    -0.129562378, -0.133590698, -0.137298584, -0.140670776, -0.143676758,
    -0.146255493, -0.148422241, -0.150115967, -0.151306152, -0.151962280,
    -0.152069092, -0.151596069, -0.150497437, -0.148773193, -0.146362305,
    -0.143264771, -0.139450073, -0.134887695, -0.129577637, -0.123474121,
    -0.116577148, -0.108856201, -0.100311279, -0.090927124, -0.080688477,
    -0.069595337, -0.057617187, -0.044784546, -0.031082153, -0.016510010,
    -0.001068115, 0.015228271,  0.032379150,  0.050354004,  0.069168091,
    0.088775635,  0.109161377,  0.130310059,  0.152206421,  0.174789429,
    0.198059082,  0.221984863,  0.246505737,  0.271591187,  0.297210693,
    0.323318481,  0.349868774,  0.376800537,  0.404083252,  0.431655884,
    0.459472656,  0.487472534,  0.515609741,  0.543823242,  0.572036743,
    0.600219727,  0.628295898,  0.656219482,  0.683914185,  0.711318970,
    0.738372803,  0.765029907,  0.791213989,  0.816864014,  0.841949463,
    0.866363525,  0.890090942,  0.913055420,  0.935195923,  0.956481934,
    0.976852417,  0.996246338,  1.014617920,  1.031936646,  1.048156738,
    1.063217163,  1.077117920,  1.089782715,  1.101211548,  1.111373901,
    1.120223999,  1.127746582,  1.133926392,  1.138763428,  1.142211914,
    1.144287109,  1.144989014};

constexpr int16 huffmanTab0[] = {0};
constexpr int16 huffmanTab1[] = {-5, -3, -1, 17, 1, 16, 0};
constexpr int16 huffmanTab2[] = {-15, -11, -9, -5, -3, -1, 34, 2, 18,
                                 -1,  33,  32, 17, -1, 1,  16, 0};
constexpr int16 huffmanTab3[] = {-13, -11, -9, -5, -3, -1, 34, 2, 18,
                                 -1,  33,  32, 16, 17, -1, 1,  0};
constexpr int16 huffmanTab5[] = {-29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50,
                                 49,  -3,  -1,  19,  3,  -1, 48, 34, -3, -1, 18,
                                 33,  -1,  2,   32,  17, -1, 1,  16, 0};
constexpr int16 huffmanTab6[] = {-25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1,
                                 50,  48,  -1,  19, 49, -3, -1, 34, 2, 18, -3,
                                 -1,  33,  32,  1,  -1, 17, -1, 16, 0};

constexpr int16 huffmanTab7[] = {
    -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84,  83,
    -1,  53,  68,  -3,  -1,  37,  82,  21, -5, -1, 81, -1, 5,  52,  -1,
    80,  -1,  67,  51,  -5,  -3,  -1,  36, 66, 20, -1, 65, 64, -11, -7,
    -3,  -1,  4,   35,  -1,  50,  3,   -1, 19, 49, -3, -1, 48, 34,  18,
    -5,  -1,  33,  -1,  2,   32,  17,  -1, 1,  16, 0};

constexpr int16 huffmanTab8[] = {
    -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83,
    -3,  -1,  53,  68,  37,  -3,  -1,  82, 5,  21, -5, -1, 81, -1, 52,
    67,  -3,  -1,  80,  51,  36,  -5,  -3, -1, 66, 20, 65, -3, -1, 4,
    64,  -1,  35,  50,  -9,  -7,  -3,  -1, 19, 49, -1, 3,  48, 34, -1,
    2,   32,  -1,  18,  33,  17,  -3,  -1, 1,  16, 0};

constexpr int16 huffmanTab9[] = {
    -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1,
    84,  5,   -3,  -1,  68,  37,  -1, 82, 21, -3, -1, 81, 52, -1, 67,
    -1,  80,  4,   -7,  -3,  -1,  36, 66, -1, 51, 64, -1, 20, 65, -5,
    -3,  -1,  35,  50,  19,  -1,  49, -1, 3,  48, -5, -3, -1, 34, 2,
    18,  -1,  33,  32,  -3,  -1,  17, 1,  -1, 16, 0};

constexpr int16 huffmanTab10[] = {
    -125, -121, -111, -83, -55, -35, -21, -13, -7,  -3, -1,  119, 103, -1,  118,
    87,   -3,   -1,   117, 102, 71,  -3,  -1,  116, 86, -1,  101, 55,  -9,  -3,
    -1,   115,  70,   -3,  -1,  85,  84,  99,  -1,  39, 114, -11, -5,  -3,  -1,
    100,  7,    112,  -1,  98,  -1,  69,  53,  -5,  -1, 6,   -1,  83,  68,  23,
    -17,  -5,   -1,   113, -1,  54,  38,  -5,  -3,  -1, 37,  82,  21,  -1,  81,
    -1,   52,   67,   -3,  -1,  22,  97,  -1,  96,  -1, 5,   80,  -19, -11, -7,
    -3,   -1,   36,   66,  -1,  51,  4,   -1,  20,  65, -3,  -1,  64,  35,  -1,
    50,   3,    -3,   -1,  19,  49,  -1,  48,  34,  -7, -3,  -1,  18,  33,  -1,
    2,    32,   17,   -1,  1,   16,  0};

constexpr int16 huffmanTab11[] = {
    -121, -113, -89, -59, -43, -27, -17, -7, -3,  -1, 119, 103, -1, 118, 117,
    -3,   -1,   102, 71,  -1,  116, -1,  87, 85,  -5, -3,  -1,  86, 101, 55,
    -1,   115,  70,  -9,  -7,  -3,  -1,  69, 84,  -1, 53,  83,  39, -1,  114,
    -1,   100,  7,   -5,  -1,  113, -1,  23, 112, -3, -1,  54,  99, -1,  96,
    -1,   68,   37,  -13, -7,  -5,  -3,  -1, 82,  5,  21,  98,  -3, -1,  38,
    6,    22,   -5,  -1,  97,  -1,  81,  52, -5,  -1, 80,  -1,  67, 51,  -1,
    36,   66,   -15, -11, -7,  -3,  -1,  20, 65,  -1, 4,   64,  -1, 35,  50,
    -1,   19,   49,  -5,  -3,  -1,  3,   48, 34,  33, -5,  -1,  18, -1,  2,
    32,   17,   -3,  -1,  1,   16,  0};

constexpr int16 huffmanTab12[] = {
    -115, -99, -73, -45, -27, -17, -9,  -5, -3, -1, 119, 103, 118, -1,  87, 117,
    -3,   -1,  102, 71,  -1,  116, 101, -3, -1, 86, 55,  -3,  -1,  115, 85, 39,
    -7,   -3,  -1,  114, 70,  -1,  100, 23, -5, -1, 113, -1,  7,   112, -1, 54,
    99,   -13, -9,  -3,  -1,  69,  84,  -1, 68, -1, 6,   5,   -1,  38,  98, -5,
    -1,   97,  -1,  22,  96,  -3,  -1,  53, 83, -1, 37,  82,  -17, -7,  -3, -1,
    21,   81,  -1,  52,  67,  -5,  -3,  -1, 80, 4,  36,  -1,  66,  20,  -3, -1,
    51,   65,  -1,  35,  50,  -11, -7,  -5, -3, -1, 64,  3,   48,  19,  -1, 49,
    34,   -1,  18,  33,  -7,  -5,  -3,  -1, 2,  32, 0,   17,  -1,  1,   16};

constexpr int16 huffmanTab13[] = {
    -509, -503, -475, -405, -333, -265, -205, -153, -115, -83, -53, -35, -21,
    -13,  -9,   -7,   -5,   -3,   -1,   254,  252,  253,  237, 255, -1,  239,
    223,  -3,   -1,   238,  207,  -1,   222,  191,  -9,   -3,  -1,  251, 206,
    -1,   220,  -1,   175,  233,  -1,   236,  221,  -9,   -5,  -3,  -1,  250,
    205,  190,  -1,   235,  159,  -3,   -1,   249,  234,  -1,  189, 219, -17,
    -9,   -3,   -1,   143,  248,  -1,   204,  -1,   174,  158, -5,  -1,  142,
    -1,   127,  126,  247,  -5,   -1,   218,  -1,   173,  188, -3,  -1,  203,
    246,  111,  -15,  -7,   -3,   -1,   232,  95,   -1,   157, 217, -3,  -1,
    245,  231,  -1,   172,  187,  -9,   -3,   -1,   79,   244, -3,  -1,  202,
    230,  243,  -1,   63,   -1,   141,  216,  -21,  -9,   -3,  -1,  47,  242,
    -3,   -1,   110,  156,  15,   -5,   -3,   -1,   201,  94,  171, -3,  -1,
    125,  215,  78,   -11,  -5,   -3,   -1,   200,  214,  62,  -1,  185, -1,
    155,  170,  -1,   31,   241,  -23,  -13,  -5,   -1,   240, -1,  186, 229,
    -3,   -1,   228,  140,  -1,   109,  227,  -5,   -1,   226, -1,  46,  14,
    -1,   30,   225,  -15,  -7,   -3,   -1,   224,  93,   -1,  213, 124, -3,
    -1,   199,  77,   -1,   139,  184,  -7,   -3,   -1,   212, 154, -1,  169,
    108,  -1,   198,  61,   -37,  -21,  -9,   -5,   -3,   -1,  211, 123, 45,
    -1,   210,  29,   -5,   -1,   183,  -1,   92,   197,  -3,  -1,  153, 122,
    195,  -7,   -5,   -3,   -1,   167,  151,  75,   209,  -3,  -1,  13,  208,
    -1,   138,  168,  -11,  -7,   -3,   -1,   76,   196,  -1,  107, 182, -1,
    60,   44,   -3,   -1,   194,  91,   -3,   -1,   181,  137, 28,  -43, -23,
    -11,  -5,   -1,   193,  -1,   152,  12,   -1,   192,  -1,  180, 106, -5,
    -3,   -1,   166,  121,  59,   -1,   179,  -1,   136,  90,  -11, -5,  -1,
    43,   -1,   165,  105,  -1,   164,  -1,   120,  135,  -5,  -1,  148, -1,
    119,  118,  178,  -11,  -3,   -1,   27,   177,  -3,   -1,  11,  176, -1,
    150,  74,   -7,   -3,   -1,   58,   163,  -1,   89,   149, -1,  42,  162,
    -47,  -23,  -9,   -3,   -1,   26,   161,  -3,   -1,   10,  104, 160, -5,
    -3,   -1,   134,  73,   147,  -3,   -1,   57,   88,   -1,  133, 103, -9,
    -3,   -1,   41,   146,  -3,   -1,   87,   117,  56,   -5,  -1,  131, -1,
    102,  71,   -3,   -1,   116,  86,   -1,   101,  115,  -11, -3,  -1,  25,
    145,  -3,   -1,   9,    144,  -1,   72,   132,  -7,   -5,  -1,  114, -1,
    70,   100,  40,   -1,   130,  24,   -41,  -27,  -11,  -5,  -3,  -1,  55,
    39,   23,   -1,   113,  -1,   85,   7,    -7,   -3,   -1,  112, 54,  -1,
    99,   69,   -3,   -1,   84,   38,   -1,   98,   53,   -5,  -1,  129, -1,
    8,    128,  -3,   -1,   22,   97,   -1,   6,    96,   -13, -9,  -5,  -3,
    -1,   83,   68,   37,   -1,   82,   5,    -1,   21,   81,  -7,  -3,  -1,
    52,   67,   -1,   80,   36,   -3,   -1,   66,   51,   20,  -19, -11, -5,
    -1,   65,   -1,   4,    64,   -3,   -1,   35,   50,   19,  -3,  -1,  49,
    3,    -1,   48,   34,   -3,   -1,   18,   33,   -1,   2,   32,  -3,  -1,
    17,   1,    16,   0};

constexpr int16 huffmanTab15[] = {
    -495, -445, -355, -263, -183, -115, -77, -43, -27, -13, -7,  -3,  -1,  255,
    239,  -1,   254,  223,  -1,   238,  -1,  253, 207, -7,  -3,  -1,  252, 222,
    -1,   237,  191,  -1,   251,  -1,   206, 236, -7,  -3,  -1,  221, 175, -1,
    250,  190,  -3,   -1,   235,  205,  -1,  220, 159, -15, -7,  -3,  -1,  249,
    234,  -1,   189,  219,  -3,   -1,   143, 248, -1,  204, 158, -7,  -3,  -1,
    233,  127,  -1,   247,  173,  -3,   -1,  218, 188, -1,  111, -1,  174, 15,
    -19,  -11,  -3,   -1,   203,  246,  -3,  -1,  142, 232, -1,  95,  157, -3,
    -1,   245,  126,  -1,   231,  172,  -9,  -3,  -1,  202, 187, -3,  -1,  217,
    141,  79,   -3,   -1,   244,  63,   -1,  243, 216, -33, -17, -9,  -3,  -1,
    230,  47,   -1,   242,  -1,   110,  240, -3,  -1,  31,  241, -1,  156, 201,
    -7,   -3,   -1,   94,   171,  -1,   186, 229, -3,  -1,  125, 215, -1,  78,
    228,  -15,  -7,   -3,   -1,   140,  200, -1,  62,  109, -3,  -1,  214, 227,
    -1,   155,  185,  -7,   -3,   -1,   46,  170, -1,  226, 30,  -5,  -1,  225,
    -1,   14,   224,  -1,   93,   213,  -45, -25, -13, -7,  -3,  -1,  124, 199,
    -1,   77,   139,  -1,   212,  -1,   184, 154, -7,  -3,  -1,  169, 108, -1,
    198,  61,   -1,   211,  210,  -9,   -5,  -3,  -1,  45,  13,  29,  -1,  123,
    183,  -5,   -1,   209,  -1,   92,   208, -1,  197, 138, -17, -7,  -3,  -1,
    168,  76,   -1,   196,  107,  -5,   -1,  182, -1,  153, 12,  -1,  60,  195,
    -9,   -3,   -1,   122,  167,  -1,   166, -1,  192, 11,  -1,  194, -1,  44,
    91,   -55,  -29,  -15,  -7,   -3,   -1,  181, 28,  -1,  137, 152, -3,  -1,
    193,  75,   -1,   180,  106,  -5,   -3,  -1,  59,  121, 179, -3,  -1,  151,
    136,  -1,   43,   90,   -11,  -5,   -1,  178, -1,  165, 27,  -1,  177, -1,
    176,  105,  -7,   -3,   -1,   150,  74,  -1,  164, 120, -3,  -1,  135, 58,
    163,  -17,  -7,   -3,   -1,   89,   149, -1,  42,  162, -3,  -1,  26,  161,
    -3,   -1,   10,   160,  104,  -7,   -3,  -1,  134, 73,  -1,  148, 57,  -5,
    -1,   147,  -1,   119,  9,    -1,   88,  133, -53, -29, -13, -7,  -3,  -1,
    41,   103,  -1,   118,  146,  -1,   145, -1,  25,  144, -7,  -3,  -1,  72,
    132,  -1,   87,   117,  -3,   -1,   56,  131, -1,  102, 71,  -7,  -3,  -1,
    40,   130,  -1,   24,   129,  -7,   -3,  -1,  116, 8,   -1,  128, 86,  -3,
    -1,   101,  55,   -1,   115,  70,   -17, -7,  -3,  -1,  39,  114, -1,  100,
    23,   -3,   -1,   85,   113,  -3,   -1,  7,   112, 54,  -7,  -3,  -1,  99,
    69,   -1,   84,   38,   -3,   -1,   98,  22,  -3,  -1,  6,   96,  53,  -33,
    -19,  -9,   -5,   -1,   97,   -1,   83,  68,  -1,  37,  82,  -3,  -1,  21,
    81,   -3,   -1,   5,    80,   52,   -7,  -3,  -1,  67,  36,  -1,  66,  51,
    -1,   65,   -1,   20,   4,    -9,   -3,  -1,  35,  50,  -3,  -1,  64,  3,
    19,   -3,   -1,   49,   48,   34,   -9,  -7,  -3,  -1,  18,  33,  -1,  2,
    32,   17,   -3,   -1,   1,    16,   0};

constexpr int16 huffmanTab16[] = {
    -509, -503, -461, -323, -103, -37, -27, -15, -7,  -3,  -1,  239, 254, -1,
    223,  253,  -3,   -1,   207,  252, -1,  191, 251, -5,  -1,  175, -1,  250,
    159,  -3,   -1,   249,  248,  143, -7,  -3,  -1,  127, 247, -1,  111, 246,
    255,  -9,   -5,   -3,   -1,   95,  245, 79,  -1,  244, 243, -53, -1,  240,
    -1,   63,   -29,  -19,  -13,  -7,  -5,  -1,  206, -1,  236, 221, 222, -1,
    233,  -1,   234,  217,  -1,   238, -1,  237, 235, -3,  -1,  190, 205, -3,
    -1,   220,  219,  174,  -11,  -5,  -1,  204, -1,  173, 218, -3,  -1,  126,
    172,  202,  -5,   -3,   -1,   201, 125, 94,  189, 242, -93, -5,  -3,  -1,
    47,   15,   31,   -1,   241,  -49, -25, -13, -5,  -1,  158, -1,  188, 203,
    -3,   -1,   142,  232,  -1,   157, 231, -7,  -3,  -1,  187, 141, -1,  216,
    110,  -1,   230,  156,  -13,  -7,  -3,  -1,  171, 186, -1,  229, 215, -1,
    78,   -1,   228,  140,  -3,   -1,  200, 62,  -1,  109, -1,  214, 155, -19,
    -11,  -5,   -3,   -1,   185,  170, 225, -1,  212, -1,  184, 169, -5,  -1,
    123,  -1,   183,  208,  227,  -7,  -3,  -1,  14,  224, -1,  93,  213, -3,
    -1,   124,  199,  -1,   77,   139, -75, -45, -27, -13, -7,  -3,  -1,  154,
    108,  -1,   198,  61,   -3,   -1,  92,  197, 13,  -7,  -3,  -1,  138, 168,
    -1,   153,  76,   -3,   -1,   182, 122, 60,  -11, -5,  -3,  -1,  91,  137,
    28,   -1,   192,  -1,   152,  121, -1,  226, -1,  46,  30,  -15, -7,  -3,
    -1,   211,  45,   -1,   210,  209, -5,  -1,  59,  -1,  151, 136, 29,  -7,
    -3,   -1,   196,  107,  -1,   195, 167, -1,  44,  -1,  194, 181, -23, -13,
    -7,   -3,   -1,   193,  12,   -1,  75,  180, -3,  -1,  106, 166, 179, -5,
    -3,   -1,   90,   165,  43,   -1,  178, 27,  -13, -5,  -1,  177, -1,  11,
    176,  -3,   -1,   105,  150,  -1,  74,  164, -5,  -3,  -1,  120, 135, 163,
    -3,   -1,   58,   89,   42,   -97, -57, -33, -19, -11, -5,  -3,  -1,  149,
    104,  161,  -3,   -1,   134,  119, 148, -5,  -3,  -1,  73,  87,  103, 162,
    -5,   -1,   26,   -1,   10,   160, -3,  -1,  57,  147, -1,  88,  133, -9,
    -3,   -1,   41,   146,  -3,   -1,  118, 9,   25,  -5,  -1,  145, -1,  144,
    72,   -3,   -1,   132,  117,  -1,  56,  131, -21, -11, -5,  -3,  -1,  102,
    40,   130,  -3,   -1,   71,   116, 24,  -3,  -1,  129, 128, -3,  -1,  8,
    86,   55,   -9,   -5,   -1,   115, -1,  101, 70,  -1,  39,  114, -5,  -3,
    -1,   100,  85,   7,    23,   -23, -13, -5,  -1,  113, -1,  112, 54,  -3,
    -1,   99,   69,   -1,   84,   38,  -3,  -1,  98,  22,  -1,  97,  -1,  6,
    96,   -9,   -5,   -1,   83,   -1,  53,  68,  -1,  37,  82,  -1,  81,  -1,
    21,   5,    -33,  -23,  -13,  -7,  -3,  -1,  52,  67,  -1,  80,  36,  -3,
    -1,   66,   51,   20,   -5,   -1,  65,  -1,  4,   64,  -1,  35,  50,  -3,
    -1,   19,   49,   -3,   -1,   3,   48,  34,  -3,  -1,  18,  33,  -1,  2,
    32,   -3,   -1,   17,   1,    16,  0};

constexpr int16 huffmanTab24[] = {
    -451, -117, -43, -25, -15, -7,  -3,  -1,   239,  254, -1,  223, 253, -3,
    -1,   207,  252, -1,  191, 251, -5,  -1,   250,  -1,  175, 159, -1,  249,
    248,  -9,   -5,  -3,  -1,  143, 127, 247,  -1,   111, 246, -3,  -1,  95,
    245,  -1,   79,  244, -71, -7,  -3,  -1,   63,   243, -1,  47,  242, -5,
    -1,   241,  -1,  31,  240, -25, -9,  -1,   15,   -3,  -1,  238, 222, -1,
    237,  206,  -7,  -3,  -1,  236, 221, -1,   190,  235, -3,  -1,  205, 220,
    -1,   174,  234, -15, -7,  -3,  -1,  189,  219,  -1,  204, 158, -3,  -1,
    233,  173,  -1,  218, 188, -7,  -3,  -1,   203,  142, -1,  232, 157, -3,
    -1,   217,  126, -1,  231, 172, 255, -235, -143, -77, -45, -25, -15, -7,
    -3,   -1,   202, 187, -1,  141, 216, -5,   -3,   -1,  14,  224, 13,  230,
    -5,   -3,   -1,  110, 156, 201, -1,  94,   186,  -9,  -5,  -1,  229, -1,
    171,  125,  -1,  215, 228, -3,  -1,  140,  200,  -3,  -1,  78,  46,  62,
    -15,  -7,   -3,  -1,  109, 214, -1,  227,  155,  -3,  -1,  185, 170, -1,
    226,  30,   -7,  -3,  -1,  225, 93,  -1,   213,  124, -3,  -1,  199, 77,
    -1,   139,  184, -31, -15, -7,  -3,  -1,   212,  154, -1,  169, 108, -3,
    -1,   198,  61,  -1,  211, 45,  -7,  -3,   -1,   210, 29,  -1,  123, 183,
    -3,   -1,   209, 92,  -1,  197, 138, -17,  -7,   -3,  -1,  168, 153, -1,
    76,   196,  -3,  -1,  107, 182, -3,  -1,   208,  12,  60,  -7,  -3,  -1,
    195,  122,  -1,  167, 44,  -3,  -1,  194,  91,   -1,  181, 28,  -57, -35,
    -19,  -7,   -3,  -1,  137, 152, -1,  193,  75,   -5,  -3,  -1,  192, 11,
    59,   -3,   -1,  176, 10,  26,  -5,  -1,   180,  -1,  106, 166, -3,  -1,
    121,  151,  -3,  -1,  160, 9,   144, -9,   -3,   -1,  179, 136, -3,  -1,
    43,   90,   178, -7,  -3,  -1,  165, 27,   -1,   177, 105, -1,  150, 164,
    -17,  -9,   -5,  -3,  -1,  74,  120, 135,  -1,   58,  163, -3,  -1,  89,
    149,  -1,   42,  162, -7,  -3,  -1,  161,  104,  -1,  134, 119, -3,  -1,
    73,   148,  -1,  57,  147, -63, -31, -15,  -7,   -3,  -1,  88,  133, -1,
    41,   103,  -3,  -1,  118, 146, -1,  25,   145,  -7,  -3,  -1,  72,  132,
    -1,   87,   117, -3,  -1,  56,  131, -1,   102,  40,  -17, -7,  -3,  -1,
    130,  24,   -1,  71,  116, -5,  -1,  129,  -1,   8,   128, -1,  86,  101,
    -7,   -5,   -1,  23,  -1,  7,   112, 115,  -3,   -1,  55,  39,  114, -15,
    -7,   -3,   -1,  70,  100, -1,  85,  113,  -3,   -1,  54,  99,  -1,  69,
    84,   -7,   -3,  -1,  38,  98,  -1,  22,   97,   -5,  -3,  -1,  6,   96,
    53,   -1,   83,  68,  -51, -37, -23, -15,  -9,   -3,  -1,  37,  82,  -1,
    21,   -1,   5,   80,  -1,  81,  -1,  52,   67,   -3,  -1,  36,  66,  -1,
    51,   20,   -9,  -5,  -1,  65,  -1,  4,    64,   -1,  35,  50,  -1,  19,
    49,   -7,   -5,  -3,  -1,  3,   48,  34,   18,   -1,  33,  -1,  2,   32,
    -3,   -1,   17,  1,   -1,  16,  0};

struct BitsToTableMap {
  uint32 bits;
  const int16 *table;
};

constexpr BitsToTableMap huffmanTables1[] = {
    {0, huffmanTab0},   {0, huffmanTab1},   {0, huffmanTab2},
    {0, huffmanTab3},   {0, huffmanTab0},   {0, huffmanTab5},
    {0, huffmanTab6},   {0, huffmanTab7},   {0, huffmanTab8},
    {0, huffmanTab9},   {0, huffmanTab10},  {0, huffmanTab11},
    {0, huffmanTab12},  {0, huffmanTab13},  {0, huffmanTab0},
    {0, huffmanTab15},  {1, huffmanTab16},  {2, huffmanTab16},
    {3, huffmanTab16},  {4, huffmanTab16},  {6, huffmanTab16},
    {8, huffmanTab16},  {10, huffmanTab16}, {13, huffmanTab16},
    {4, huffmanTab24},  {5, huffmanTab24},  {6, huffmanTab24},
    {7, huffmanTab24},  {8, huffmanTab24},  {9, huffmanTab24},
    {11, huffmanTab24}, {13, huffmanTab24}};

constexpr int16 huffmanTabC0[] = {-29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14,
                                  -3,  -1,  7,   5,  9,  -3, -1, 6,  3,  -1, 10,
                                  12,  -3,  -1,  2,  1,  -1, 4,  8,  0};
constexpr int16 huffmanTabC1[] = {-15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1,
                                  11,  10, -1, 9,  8,  -7, -3, -1, 7,  6,  -1,
                                  5,   4,  -3, -1, 3,  2,  -1, 1,  0};

constexpr BitsToTableMap huffmanTables2[] = {{0, huffmanTabC0},
                                             {0, huffmanTabC1}};

//==============================================================================
struct VBRTagData {
  bool read(const uint8 *data) noexcept {
    flags = 0;

    const int layer = (data[1] >> 1) & 3;
    if (layer != 1)
      return false;

    const int type = (data[1] >> 3) & 1;
    const int sampleRateIndex = (data[2] >> 2) & 3;
    const int mode = (data[3] >> 6) & 3;

    static constexpr short bitRates[3][16] = {
        {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,
         -1}, // MPEG2
        {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
         -1}, // MPEG1
        {0, 8, 16, 24, 32, 40, 48, 56, 64, -1, -1, -1, -1, -1, -1,
         -1}, // MPEG 2.5
    };

    const int bitrate = bitRates[type][((data[2] >> 4) & 15)];

    const int sampleRates[3][4] = {
        {22050, 24000, 16000, -1}, // MPEG2
        {44100, 48000, 32000, -1}, // MPEG1
        {11025, 12000, 8000, -1},  // MPEG2.5
    };

    if ((data[1] >> 4) == 0xe)
      sampleRate = sampleRates[2][sampleRateIndex];
    else
      sampleRate = sampleRates[type][sampleRateIndex];

    data += type != 0 ? (mode != 3 ? (32 + 4) : (17 + 4))
                      : (mode != 3 ? (17 + 4) : (9 + 4));

    if (!isVbrTag(data))
      return false;

    data += 4;
    flags = ByteOrder::bigEndianInt(data);
    data += 4;

    if (flags & 1) {
      frames = ByteOrder::bigEndianInt(data);
      data += 4;
    }

    if (flags & 2) {
      bytes = ByteOrder::bigEndianInt(data);
      data += 4;
    }

    if (flags & 4) {
      for (int i = 0; i < 100; ++i)
        toc[i] = data[i];

      data += 100;
    }

    vbrScale = -1;

    if (flags & 8)
      vbrScale = (int)ByteOrder::bigEndianInt(data);

    headersize = ((type + 1) * 72000 * bitrate) / sampleRate;
    return true;
  }

  uint8 toc[100];
  int sampleRate, vbrScale, headersize;
  unsigned int flags, frames, bytes;

private:
  static bool isVbrTag(const uint8 *d) noexcept {
    return (d[0] == 'X' && d[1] == 'i' && d[2] == 'n' && d[3] == 'g') ||
           (d[0] == 'I' && d[1] == 'n' && d[2] == 'f' && d[3] == 'o');
  }
};

//==============================================================================
struct MP3Frame {
  MP3Frame() {
    zeromem(this, sizeof(MP3Frame));
    single = -1;
  }

  void selectLayer2Table() {
    static constexpr int translate[3][2][16] = {
        {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 0},
         {0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}},
        {{0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
         {0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
        {{0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 0},
         {0, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}};

    static const AllocationTable *const tables[] = {
        allocTable0, allocTable1, allocTable2, allocTable3, allocTable4};
    static constexpr int8 limits[] = {27, 30, 8, 12, 30};

    const int index =
        lsf ? 4 : translate[sampleRateIndex][2 - numChannels][bitrateIndex];
    layer2SubBandLimit = limits[index];
    allocationTable = tables[index];
  }

  int getFrequency() const noexcept {
    const int frequencies[] = {44100, 48000, 32000, 22050, 24000,
                               16000, 11025, 12000, 8000};
    return frequencies[sampleRateIndex];
  }

  enum class ParseSuccessful { no, yes };

  ParseSuccessful decodeHeader(const uint32 header) {
    jassert(((header >> 10) & 3) != 3);

    mpeg25 = (header & (1 << 20)) == 0;
    lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1);
    layer = (int)(4 - ((header >> 17) & 3));
    sampleRateIndex = (int)((header >> 10) & 3) + (mpeg25 ? 6 : (lsf * 3));
    crc16FollowsHeader = ((header >> 16) & 1) == 0;
    bitrateIndex = (header >> 12) & 15;
    padding = (header >> 9) & 1;
    mode = (header >> 6) & 3;
    modeExt = (header >> 4) & 3;
    // extension         = (header >> 8) & 1;
    // copyright         = (header >> 3) & 1;
    // original          = (header >> 2) & 1;
    // emphasis          = header & 3;
    numChannels = (mode == 3) ? 1 : 2;

    static constexpr int frameSizes[2][3][16] = {
        {{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448},
         {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},
         {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},

        {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
         {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
         {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}}};

    if (bitrateIndex == 0) {
      jassertfalse; // This means the file is using "free format". Apparently
                    // very few decoders support this mode, and this one
                    // certainly doesn't handle it correctly!
      frameSize = 0;
      return ParseSuccessful::no;
    }

    switch (layer) {
    case 1:
      frameSize =
          (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() +
            padding) *
           4) -
          4;
      break;
    case 2:
      frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() +
                  (padding - 4);
      break;
    case 3:
      frameSize = (bitrateIndex == 0)
                      ? 0
                      : ((frameSizes[lsf][2][bitrateIndex] * 144000) /
                             (getFrequency() << lsf) +
                         (padding - 4));
      break;
    default:
      break;
    }

    return ParseSuccessful::yes;
  }

  int numSamples() const {
    switch (layer) {
    case 1:
      return 384;
    case 3:
      if (lsf)
        return 576;
    default:
      return 1152;
    }
  }

  int layer, frameSize, numChannels, single;
  int lsf;     // 0 = mpeg-1, 1 = mpeg-2/LSF
  bool mpeg25; // true = mpeg-2.5, false = mpeg-1/2
  bool crc16FollowsHeader;
  int bitrateIndex, sampleRateIndex, padding;
  int mode, modeExt, layer2SubBandLimit;
  enum { downSampleLimit = 32 };
  const AllocationTable *allocationTable;
};

//==============================================================================
struct Constants {
  Constants() {
    cosTables[0] = cos64;
    cosTables[1] = cos32;
    cosTables[2] = cos16;
    cosTables[3] = cos8;
    cosTables[4] = cos4;
    initDecodeTables();
    initLayer2Tables();
    initLayer3Tables();
  }

  const uint8 *getGroupTable(const int16 d1,
                             const uint32 index) const noexcept {
    switch (d1) {
    case 3:
      return &group3tab[3 * jmin(index, 3u * 3u * 3u)];
    case 5:
      return &group5tab[3 * jmin(index, 5u * 5u * 5u)];
    case 9:
      return &group9tab[3 * jmin(index, 9u * 9u * 9u)];
    default:
      break;
    }

    static constexpr uint8 dummy[] = {0, 0, 0};
    return dummy;
  }

  float muls[27][64];
  float nToThe4Over3[8207];
  float antiAliasingCa[8], antiAliasingCs[8];
  float win[4][36];
  float win1[4][36];
  float powToGains[256 + 118 + 4];
  int longLimit[9][23];
  int shortLimit[9][14];
  float tan1_1[16], tan2_1[16], tan1_2[16], tan2_2[16];
  float pow1_1[2][16], pow2_1[2][16], pow1_2[2][16], pow2_2[2][16];
  int *map[9][3];
  int *mapEnd[9][3];
  uint32 nLength2[512];
  uint32 iLength2[256];
  float decodeWin[512 + 32];
  float *cosTables[5];

private:
  int mapbuf0[9][152];
  int mapbuf1[9][156];
  int mapbuf2[9][44];
  float cos64[16], cos32[8], cos16[4], cos8[2], cos4[1];
  uint8 group3tab[32 * 3];
  uint8 group5tab[128 * 3];
  uint8 group9tab[1024 * 3];

  void initDecodeTables() {
    int i, j, scaleval = -1;
    float *table = decodeWin;

    for (i = 0; i < 5; ++i) {
      int kr = 0x10 >> i;
      int divv = 0x40 >> i;
      float *costab = cosTables[i];

      for (int k = 0; k < kr; ++k)
        costab[k] = (float)(1.0 / (2.0 * std::cos(MathConstants<double>::pi *
                                                  (k * 2 + 1) / divv)));
    }

    for (i = 0, j = 0; i < 256; ++i, ++j, table += 32) {
      if (table < decodeWin + 512 + 16)
        table[16] = table[0] = (float)(decodeWindow[j] * scaleval);
      if (i % 32 == 31)
        table -= 1023;
      if (i % 64 == 63)
        scaleval = -scaleval;
    }

    for (; i < 512; ++i, --j, table += 32) {
      if (table < decodeWin + 512 + 16)
        table[16] = table[0] = (float)(decodeWindow[j] * scaleval);

      if (i % 32 == 31)
        table -= 1023;
      if (i % 64 == 63)
        scaleval = -scaleval;
    }
  }

  void initLayer2Tables() {
    static const uint8 base[3][9] = {
        {1, 0, 2}, {17, 18, 0, 19, 20}, {21, 1, 22, 23, 0, 24, 25, 2, 26}};

    static constexpr int tableLengths[] = {3, 5, 9};
    static uint8 *tables[] = {group3tab, group5tab, group9tab};

    for (int i = 0; i < 3; ++i) {
      uint8 *table = tables[i];
      const int len = tableLengths[i];

      for (int j = 0; j < len; ++j)
        for (int k = 0; k < len; ++k)
          for (int l = 0; l < len; ++l) {
            *table++ = base[i][l];
            *table++ = base[i][k];
            *table++ = base[i][j];
          }
    }

    for (int k = 0; k < 27; ++k) {
      static constexpr double multipliers[] = {0,
                                               -2.0 / 3.0,
                                               2.0 / 3.0,
                                               2.0 / 7.0,
                                               2.0 / 15.0,
                                               2.0 / 31.0,
                                               2.0 / 63.0,
                                               2.0 / 127.0,
                                               2.0 / 255.0,
                                               2.0 / 511.0,
                                               2.0 / 1023.0,
                                               2.0 / 2047.0,
                                               2.0 / 4095.0,
                                               2.0 / 8191.0,
                                               2.0 / 16383.0,
                                               2.0 / 32767.0,
                                               2.0 / 65535.0,
                                               -4.0 / 5.0,
                                               -2.0 / 5.0,
                                               2.0 / 5.0,
                                               4.0 / 5.0,
                                               -8.0 / 9.0,
                                               -4.0 / 9.0,
                                               -2.0 / 9.0,
                                               2.0 / 9.0,
                                               4.0 / 9.0,
                                               8.0 / 9.0};

      float *table = muls[k];
      for (int j = 3, i = 0; i < 63; ++i, --j)
        *table++ = (float)(multipliers[k] * std::pow(2.0, j / 3.0));
      *table++ = 0;
    }
  }

  void initLayer3Tables() {
    int i, j;
    for (i = -256; i < 118 + 4; ++i)
      powToGains[i + 256] = (float)std::pow(2.0, -0.25 * (i + 210));

    for (i = 0; i < 8207; ++i)
      nToThe4Over3[i] = (float)std::pow((double)i, 4.0 / 3.0);

    for (i = 0; i < 8; ++i) {
      static constexpr double Ci[] = {-0.6,   -0.535, -0.33,   -0.185,
                                      -0.095, -0.041, -0.0142, -0.0037};
      const double sq = sqrt(1.0 + Ci[i] * Ci[i]);
      antiAliasingCs[i] = (float)(1.0 / sq);
      antiAliasingCa[i] = (float)(Ci[i] / sq);
    }

    for (i = 0; i < 18; ++i) {
      win[0][i] = win[1][i] =
          (float)(0.5 *
                  std::sin(MathConstants<double>::pi / 72.0 * (2 * i + 1)) /
                  std::cos(MathConstants<double>::pi * (2 * i + 19) / 72.0));
      win[0][i + 18] = win[3][i + 18] =
          (float)(0.5 *
                  std::sin(MathConstants<double>::pi / 72.0 *
                           (2 * (i + 18) + 1)) /
                  std::cos(MathConstants<double>::pi * (2 * (i + 18) + 19) /
                           72.0));
    }

    const double piOver72 = MathConstants<double>::pi / 72.0;

    for (i = 0; i < 6; ++i) {
      win[1][i + 18] = (float)(0.5 / std::cos(piOver72 * (2 * (i + 18) + 19)));
      win[3][i + 12] = (float)(0.5 / std::cos(piOver72 * (2 * (i + 12) + 19)));
      win[1][i + 24] =
          (float)(0.5 *
                  std::sin(MathConstants<double>::pi / 24.0 * (2 * i + 13)) /
                  std::cos(piOver72 * (2 * (i + 24) + 19)));
      win[1][i + 30] = win[3][i] = 0;
      win[3][i + 6] =
          (float)(0.5 *
                  std::sin(MathConstants<double>::pi / 24.0 * (2 * i + 1)) /
                  std::cos(piOver72 * (2 * (i + 6) + 19)));
    }

    for (i = 0; i < 12; ++i)
      win[2][i] =
          (float)(0.5 *
                  std::sin(MathConstants<double>::pi / 24.0 * (2 * i + 1)) /
                  std::cos(MathConstants<double>::pi * (2 * i + 7) / 24.0));

    for (j = 0; j < 4; ++j) {
      static constexpr int len[4] = {36, 36, 12, 36};
      for (i = 0; i < len[j]; i += 2)
        win1[j][i] = win[j][i];
      for (i = 1; i < len[j]; i += 2)
        win1[j][i] = -win[j][i];
    }

    const double sqrt2 = 1.41421356237309504880168872420969808;

    for (i = 0; i < 16; ++i) {
      const double t = std::tan(i * MathConstants<double>::pi / 12.0);
      tan1_1[i] = (float)(t / (1.0 + t));
      tan2_1[i] = (float)(1.0 / (1.0 + t));
      tan1_2[i] = (float)(sqrt2 * t / (1.0 + t));
      tan2_2[i] = (float)(sqrt2 / (1.0 + t));

      for (j = 0; j < 2; ++j) {
        double p1 = 1.0, p2 = 1.0;

        if (i > 0) {
          const double base = std::pow(2.0, -0.25 * (j + 1));

          if (i & 1)
            p1 = std::pow(base, (i + 1) * 0.5);
          else
            p2 = std::pow(base, i * 0.5);
        }

        pow1_1[j][i] = (float)p1;
        pow2_1[j][i] = (float)p2;
        pow1_2[j][i] = (float)(sqrt2 * p1);
        pow2_2[j][i] = (float)(sqrt2 * p2);
      }
    }

    for (j = 0; j < 9; ++j) {
      const BandInfoStruct &bi = bandInfo[j];
      int cb;
      int *mp = map[j][0] = mapbuf0[j];
      const int16 *bdf = bi.longDiff;

      for (i = 0, cb = 0; cb < 8; ++cb, i += *bdf++) {
        *mp++ = (*bdf) >> 1;
        *mp++ = i;
        *mp++ = 3;
        *mp++ = cb;
      }
      bdf = bi.shortDiff + 3;

      for (cb = 3; cb < 13; ++cb) {
        const int l = (*bdf++) >> 1;

        for (int lwin = 0; lwin < 3; ++lwin) {
          *mp++ = l;
          *mp++ = i + lwin;
          *mp++ = lwin;
          *mp++ = cb;
        }
        i += 6 * l;
      }

      mapEnd[j][0] = mp;
      mp = map[j][1] = mapbuf1[j];
      bdf = bi.shortDiff;

      for (i = 0, cb = 0; cb < 13; ++cb) {
        const int l = (*bdf++) >> 1;
        for (int lwin = 0; lwin < 3; ++lwin) {
          *mp++ = l;
          *mp++ = i + lwin;
          *mp++ = lwin;
          *mp++ = cb;
        }
        i += 6 * l;
      }
      mapEnd[j][1] = mp;

      mp = map[j][2] = mapbuf2[j];
      bdf = bi.longDiff;
      for (cb = 0; cb < 22; ++cb) {
        *mp++ = (*bdf++) >> 1;
        *mp++ = cb;
      }
      mapEnd[j][2] = mp;
    }

    for (j = 0; j < 9; ++j) {
      for (i = 0; i < 23; ++i)
        longLimit[j][i] = jmin(32, (bandInfo[j].longIndex[i] - 1 + 8) / 18 + 1);
      for (i = 0; i < 14; ++i)
        shortLimit[j][i] = jmin(32, (bandInfo[j].shortIndex[i] - 1) / 18 + 1);
    }

    for (i = 0; i < 5; ++i)
      for (j = 0; j < 6; ++j)
        for (int k = 0; k < 6; ++k) {
          const int n = k + j * 6 + i * 36;
          iLength2[n] = (unsigned int)(i | (j << 3) | (k << 6) | (3 << 12));
        }

    for (i = 0; i < 4; ++i)
      for (j = 0; j < 4; ++j)
        for (int k = 0; k < 4; ++k) {
          const int n = k + j * 4 + i * 16;
          iLength2[n + 180] =
              (unsigned int)(i | (j << 3) | (k << 6) | (4 << 12));
        }

    for (i = 0; i < 4; ++i)
      for (j = 0; j < 3; ++j) {
        const int n = j + i * 3;
        iLength2[n + 244] = (unsigned int)(i | (j << 3) | (5 << 12));
        nLength2[n + 500] =
            (unsigned int)(i | (j << 3) | (2 << 12) | (1 << 15));
      }

    for (i = 0; i < 5; ++i)
      for (j = 0; j < 5; ++j)
        for (int k = 0; k < 4; ++k)
          for (int l = 0; l < 4; ++l) {
            const int n = l + k * 4 + j * 16 + i * 80;
            nLength2[n] =
                (unsigned int)(i | (j << 3) | (k << 6) | (l << 9) | (0 << 12));
          }

    for (i = 0; i < 5; ++i)
      for (j = 0; j < 5; ++j)
        for (int k = 0; k < 4; ++k) {
          const int n = k + j * 4 + i * 20;
          nLength2[n + 400] =
              (unsigned int)(i | (j << 3) | (k << 6) | (1 << 12));
        }
  }
};

static const Constants constants;

//==============================================================================
struct Layer3SideInfo {
  struct Info {
    void doAntialias(float xr[32][18]) const noexcept {
      float *xr1 = xr[1];
      int sb;

      if (blockType == 2) {
        if (mixedBlockFlag == 0)
          return;

        sb = 1;
      } else
        sb = (int)maxb - 1;

      for (; sb != 0; --sb, xr1 += 10) {
        auto *cs = constants.antiAliasingCs;
        auto *ca = constants.antiAliasingCa;
        auto *xr2 = xr1;

        for (int ss = 7; ss >= 0; --ss) {
          const float bu = *--xr2, bd = *xr1;
          *xr2 = (bu * *cs) - (bd * *ca);
          *xr1++ = (bd * *cs++) + (bu * *ca++);
        }
      }
    }

    void doIStereo(float xrBuffer[2][32][18], const int *scaleFactors,
                   int sampleRate, bool msStereo, int lsf) const noexcept {
      float(*xr)[32 * 18] = (float(*)[32 * 18]) xrBuffer;
      auto &bi = bandInfo[sampleRate];
      const float *tabl1, *tabl2;

      if (lsf != 0) {
        auto p = scaleFactorCompression & 1;

        if (msStereo) {
          tabl1 = constants.pow1_2[p];
          tabl2 = constants.pow2_2[p];
        } else {
          tabl1 = constants.pow1_1[p];
          tabl2 = constants.pow2_1[p];
        }
      } else {
        if (msStereo) {
          tabl1 = constants.tan1_2;
          tabl2 = constants.tan2_2;
        } else {
          tabl1 = constants.tan1_1;
          tabl2 = constants.tan2_1;
        }
      }

      if (blockType == 2) {
        bool doL = mixedBlockFlag != 0;

        for (uint32 lwin = 0; lwin < 3; ++lwin) {
          uint32 sfb = maxBand[lwin];
          doL = doL && (sfb <= 3);

          for (; sfb < 12; ++sfb) {
            auto p = scaleFactors[sfb * 3 + lwin - mixedBlockFlag];

            if (p != 7) {
              auto t1 = tabl1[p];
              auto t2 = tabl2[p];
              int sb = bi.shortDiff[sfb];
              auto index = (uint32)sb + lwin;

              for (; sb > 0; --sb, index += 3) {
                float v = xr[0][index];
                xr[0][index] = v * t1;
                xr[1][index] = v * t2;
              }
            }
          }

          auto p = scaleFactors[11 * 3 + lwin - mixedBlockFlag];

          if (p != 7) {
            auto t1 = tabl1[p];
            auto t2 = tabl2[p];
            int sb = bi.shortDiff[12];
            auto index = (uint32)sb + lwin;

            for (; sb > 0; --sb, index += 3) {
              float v = xr[0][index];
              xr[0][index] = v * t1;
              xr[1][index] = v * t2;
            }
          }
        }

        if (doL) {
          int index = bi.longIndex[maxBandl];

          for (uint32 sfb = maxBandl; sfb < 8; ++sfb) {
            int sb = bi.longDiff[sfb];
            auto p = scaleFactors[sfb];

            if (p != 7) {
              auto t1 = tabl1[p];
              auto t2 = tabl2[p];

              for (; sb > 0; --sb, ++index) {
                float v = xr[0][index];
                xr[0][index] = v * t1;
                xr[1][index] = v * t2;
              }
            } else
              index += sb;
          }
        }
      } else {
        int index = bi.longIndex[maxBandl];

        for (uint32 sfb = maxBandl; sfb < 21; ++sfb) {
          int sb = bi.longDiff[sfb];
          auto p = scaleFactors[sfb];

          if (p != 7) {
            auto t1 = tabl1[p];
            auto t2 = tabl2[p];

            for (; sb > 0; --sb, ++index) {
              const float v = xr[0][index];
              xr[0][index] = v * t1;
              xr[1][index] = v * t2;
            }
          } else
            index += sb;
        }

        auto p = scaleFactors[20];

        if (p != 7) {
          auto t1 = tabl1[p], t2 = tabl2[p];

          for (int sb = bi.longDiff[21]; sb > 0; --sb, ++index) {
            const float v = xr[0][index];
            xr[0][index] = v * t1;
            xr[1][index] = v * t2;
          }
        }
      }
    }

    int scfsi;
    uint32 part2_3Length, bigValues;
    uint32 scaleFactorCompression, blockType, mixedBlockFlag;
    uint32 tableSelect[3];
    uint32 maxBand[3];
    uint32 maxBandl, maxb, region1Start, region2Start;
    uint32 preflag, scaleFactorScale, count1TableSelect;
    const float *fullGain[3];
    const float *pow2gain;
  };

  struct InfoPair {
    Info gr[2];
  };
  InfoPair ch[2];

  uint32 mainDataStart, privateBits;
};

//==============================================================================
namespace DCT {
enum { subBandLimit = 32 };
static constexpr float cos6_1 = 0.866025388f;
static constexpr float cos6_2 = 0.5f;
static constexpr float cos9[] = {1.0f,         0.98480773f,  0.939692616f,
                                 0.866025388f, 0.766044438f, 0.642787635f,
                                 0.5f,         0.342020154f, 0.173648179f};
static constexpr float cos36[] = {0.501909912f, 0.517638087f, 0.551688969f,
                                  0.610387266f, 0.707106769f, 0.871723413f,
                                  1.18310082f,  1.93185163f,  5.73685646f};
static constexpr float cos12[] = {0.517638087f, 0.707106769f, 1.93185163f};

inline void dct36_0(int v, float *ts, float *out1, float *out2,
                    const float *wintab, float sum0, float sum1) noexcept {
  auto tmp = sum0 + sum1;
  out2[9 + v] = tmp * wintab[27 + v];
  out2[8 - v] = tmp * wintab[26 - v];
  sum0 -= sum1;
  ts[subBandLimit * (8 - v)] = out1[8 - v] + sum0 * wintab[8 - v];
  ts[subBandLimit * (9 + v)] = out1[9 + v] + sum0 * wintab[9 + v];
}

inline void dct36_12(int v1, int v2, float *ts, float *out1, float *out2,
                     const float *wintab, float tmp1a, float tmp1b, float tmp2a,
                     float tmp2b) noexcept {
  dct36_0(v1, ts, out1, out2, wintab, tmp1a + tmp2a,
          (tmp1b + tmp2b) * cos36[v1]);
  dct36_0(v2, ts, out1, out2, wintab, tmp2a - tmp1a,
          (tmp2b - tmp1b) * cos36[v2]);
}

static void dct36(float *in, float *out1, float *out2, const float *wintab,
                  float *ts) noexcept {
  in[17] += in[16];
  in[16] += in[15];
  in[15] += in[14];
  in[14] += in[13];
  in[13] += in[12];
  in[12] += in[11];
  in[11] += in[10];
  in[10] += in[9];
  in[9] += in[8];
  in[8] += in[7];
  in[7] += in[6];
  in[6] += in[5];
  in[5] += in[4];
  in[4] += in[3];
  in[3] += in[2];
  in[2] += in[1];
  in[1] += in[0];
  in[17] += in[15];
  in[15] += in[13];
  in[13] += in[11];
  in[11] += in[9];
  in[9] += in[7];
  in[7] += in[5];
  in[5] += in[3];
  in[3] += in[1];

  auto ta33 = in[6] * cos9[3];
  auto ta66 = in[12] * cos9[6];
  auto tb33 = in[7] * cos9[3];
  auto tb66 = in[13] * cos9[6];

  dct36_12(0, 8, ts, out1, out2, wintab,
           in[2] * cos9[1] + ta33 + in[10] * cos9[5] + in[14] * cos9[7],
           in[3] * cos9[1] + tb33 + in[11] * cos9[5] + in[15] * cos9[7],
           in[0] + in[4] * cos9[2] + in[8] * cos9[4] + ta66 + in[16] * cos9[8],
           in[1] + in[5] * cos9[2] + in[9] * cos9[4] + tb66 + in[17] * cos9[8]);

  dct36_12(1, 7, ts, out1, out2, wintab, (in[2] - in[10] - in[14]) * cos9[3],
           (in[3] - in[11] - in[15]) * cos9[3],
           (in[4] - in[8] - in[16]) * cos9[6] - in[12] + in[0],
           (in[5] - in[9] - in[17]) * cos9[6] - in[13] + in[1]);

  dct36_12(2, 6, ts, out1, out2, wintab,
           in[2] * cos9[5] - ta33 - in[10] * cos9[7] + in[14] * cos9[1],
           in[3] * cos9[5] - tb33 - in[11] * cos9[7] + in[15] * cos9[1],
           in[0] - in[4] * cos9[8] - in[8] * cos9[2] + ta66 + in[16] * cos9[4],
           in[1] - in[5] * cos9[8] - in[9] * cos9[2] + tb66 + in[17] * cos9[4]);

  dct36_12(3, 5, ts, out1, out2, wintab,
           in[2] * cos9[7] - ta33 + in[10] * cos9[1] - in[14] * cos9[5],
           in[3] * cos9[7] - tb33 + in[11] * cos9[1] - in[15] * cos9[5],
           in[0] - in[4] * cos9[4] + in[8] * cos9[8] + ta66 - in[16] * cos9[2],
           in[1] - in[5] * cos9[4] + in[9] * cos9[8] + tb66 - in[17] * cos9[2]);

  dct36_0(4, ts, out1, out2, wintab, in[0] - in[4] + in[8] - in[12] + in[16],
          (in[1] - in[5] + in[9] - in[13] + in[17]) * cos36[4]);
}

struct DCT12Inputs {
  float in0, in1, in2, in3, in4, in5;

  inline DCT12Inputs(const float *in) noexcept {
    in5 = in[5 * 3] + (in4 = in[4 * 3]);
    in4 += (in3 = in[3 * 3]);
    in3 += (in2 = in[2 * 3]);
    in2 += (in1 = in[1 * 3]);
    in1 += (in0 = in[0 * 3]);
    in5 += in3;
    in3 += in1;
    in2 *= cos6_1;
    in3 *= cos6_1;
  }

  inline void process() noexcept {
    in0 += in4 * cos6_2;
    in4 = in0 + in2;
    in0 -= in2;
    in1 += in5 * cos6_2;
    in5 = (in1 + in3) * cos12[0];
    in1 = (in1 - in3) * cos12[2];
    in3 = in4 + in5;
    in4 -= in5;
    in2 = in0 + in1;
    in0 -= in1;
  }
};

static void dct12(const float *in, float *out1, float *out2, const float *wi,
                  float *ts) noexcept {
  {
    ts[0] = out1[0];
    ts[subBandLimit * 1] = out1[1];
    ts[subBandLimit * 2] = out1[2];
    ts[subBandLimit * 3] = out1[3];
    ts[subBandLimit * 4] = out1[4];
    ts[subBandLimit * 5] = out1[5];

    DCT12Inputs inputs(in);

    {
      auto tmp1 = (inputs.in0 - inputs.in4);
      auto tmp2 = (inputs.in1 - inputs.in5) * cos12[1];
      auto tmp0 = tmp1 + tmp2;
      tmp1 -= tmp2;

      ts[16 * subBandLimit] = out1[16] + tmp0 * wi[10];
      ts[13 * subBandLimit] = out1[13] + tmp0 * wi[7];
      ts[7 * subBandLimit] = out1[7] + tmp1 * wi[1];
      ts[10 * subBandLimit] = out1[10] + tmp1 * wi[4];
    }

    inputs.process();

    ts[17 * subBandLimit] = out1[17] + inputs.in2 * wi[11];
    ts[12 * subBandLimit] = out1[12] + inputs.in2 * wi[6];
    ts[14 * subBandLimit] = out1[14] + inputs.in3 * wi[8];
    ts[15 * subBandLimit] = out1[15] + inputs.in3 * wi[9];

    ts[6 * subBandLimit] = out1[6] + inputs.in0 * wi[0];
    ts[11 * subBandLimit] = out1[11] + inputs.in0 * wi[5];
    ts[8 * subBandLimit] = out1[8] + inputs.in4 * wi[2];
    ts[9 * subBandLimit] = out1[9] + inputs.in4 * wi[3];
  }

  {
    DCT12Inputs inputs(++in);
    auto tmp1 = (inputs.in0 - inputs.in4);
    auto tmp2 = (inputs.in1 - inputs.in5) * cos12[1];
    auto tmp0 = tmp1 + tmp2;
    tmp1 -= tmp2;
    out2[4] = tmp0 * wi[10];
    out2[1] = tmp0 * wi[7];
    ts[13 * subBandLimit] += tmp1 * wi[1];
    ts[16 * subBandLimit] += tmp1 * wi[4];

    inputs.process();

    out2[5] = inputs.in2 * wi[11];
    out2[0] = inputs.in2 * wi[6];
    out2[2] = inputs.in3 * wi[8];
    out2[3] = inputs.in3 * wi[9];
    ts[12 * subBandLimit] += inputs.in0 * wi[0];
    ts[17 * subBandLimit] += inputs.in0 * wi[5];
    ts[14 * subBandLimit] += inputs.in4 * wi[2];
    ts[15 * subBandLimit] += inputs.in4 * wi[5 - 2];
  }

  {
    DCT12Inputs inputs(++in);
    out2[12] = out2[13] = out2[14] = out2[15] = out2[16] = out2[17] = 0;

    auto tmp1 = (inputs.in0 - inputs.in4);
    auto tmp2 = (inputs.in1 - inputs.in5) * cos12[1];
    auto tmp0 = tmp1 + tmp2;
    tmp1 -= tmp2;

    out2[10] = tmp0 * wi[10];
    out2[7] = tmp0 * wi[7];
    out2[1] += tmp1 * wi[1];
    out2[4] += tmp1 * wi[4];

    inputs.process();

    out2[11] = inputs.in2 * wi[11];
    out2[6] = inputs.in2 * wi[6];
    out2[8] = inputs.in3 * wi[8];
    out2[9] = inputs.in3 * wi[9];
    out2[0] += inputs.in0 * wi[0];
    out2[5] += inputs.in0 * wi[5];
    out2[2] += inputs.in4 * wi[2];
    out2[3] += inputs.in4 * wi[3];
  }
}

static void dct64(float *out0, float *out1, const float *samples) noexcept {
  float b1[32], b2[32];

  {
    auto *costab = constants.cosTables[0];
    b1[0x00] = samples[0x00] + samples[0x1F];
    b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0];
    b1[0x01] = samples[0x01] + samples[0x1E];
    b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1];
    b1[0x02] = samples[0x02] + samples[0x1D];
    b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2];
    b1[0x03] = samples[0x03] + samples[0x1C];
    b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3];
    b1[0x04] = samples[0x04] + samples[0x1B];
    b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4];
    b1[0x05] = samples[0x05] + samples[0x1A];
    b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5];
    b1[0x06] = samples[0x06] + samples[0x19];
    b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6];
    b1[0x07] = samples[0x07] + samples[0x18];
    b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7];
    b1[0x08] = samples[0x08] + samples[0x17];
    b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8];
    b1[0x09] = samples[0x09] + samples[0x16];
    b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9];
    b1[0x0A] = samples[0x0A] + samples[0x15];
    b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA];
    b1[0x0B] = samples[0x0B] + samples[0x14];
    b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB];
    b1[0x0C] = samples[0x0C] + samples[0x13];
    b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC];
    b1[0x0D] = samples[0x0D] + samples[0x12];
    b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD];
    b1[0x0E] = samples[0x0E] + samples[0x11];
    b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE];
    b1[0x0F] = samples[0x0F] + samples[0x10];
    b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF];
  }

  {
    auto *costab = constants.cosTables[1];
    b2[0x00] = b1[0x00] + b1[0x0F];
    b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0];
    b2[0x01] = b1[0x01] + b1[0x0E];
    b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1];
    b2[0x02] = b1[0x02] + b1[0x0D];
    b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2];
    b2[0x03] = b1[0x03] + b1[0x0C];
    b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3];
    b2[0x04] = b1[0x04] + b1[0x0B];
    b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4];
    b2[0x05] = b1[0x05] + b1[0x0A];
    b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5];
    b2[0x06] = b1[0x06] + b1[0x09];
    b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6];
    b2[0x07] = b1[0x07] + b1[0x08];
    b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7];
    b2[0x10] = b1[0x10] + b1[0x1F];
    b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0];
    b2[0x11] = b1[0x11] + b1[0x1E];
    b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1];
    b2[0x12] = b1[0x12] + b1[0x1D];
    b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2];
    b2[0x13] = b1[0x13] + b1[0x1C];
    b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3];
    b2[0x14] = b1[0x14] + b1[0x1B];
    b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4];
    b2[0x15] = b1[0x15] + b1[0x1A];
    b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5];
    b2[0x16] = b1[0x16] + b1[0x19];
    b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6];
    b2[0x17] = b1[0x17] + b1[0x18];
    b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7];
  }

  {
    auto *costab = constants.cosTables[2];
    b1[0x00] = b2[0x00] + b2[0x07];
    b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0];
    b1[0x01] = b2[0x01] + b2[0x06];
    b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1];
    b1[0x02] = b2[0x02] + b2[0x05];
    b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2];
    b1[0x03] = b2[0x03] + b2[0x04];
    b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3];
    b1[0x08] = b2[0x08] + b2[0x0F];
    b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0];
    b1[0x09] = b2[0x09] + b2[0x0E];
    b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1];
    b1[0x0A] = b2[0x0A] + b2[0x0D];
    b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2];
    b1[0x0B] = b2[0x0B] + b2[0x0C];
    b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3];
    b1[0x10] = b2[0x10] + b2[0x17];
    b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0];
    b1[0x11] = b2[0x11] + b2[0x16];
    b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1];
    b1[0x12] = b2[0x12] + b2[0x15];
    b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2];
    b1[0x13] = b2[0x13] + b2[0x14];
    b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3];
    b1[0x18] = b2[0x18] + b2[0x1F];
    b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0];
    b1[0x19] = b2[0x19] + b2[0x1E];
    b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1];
    b1[0x1A] = b2[0x1A] + b2[0x1D];
    b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2];
    b1[0x1B] = b2[0x1B] + b2[0x1C];
    b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3];
  }

  {
    auto cos0 = constants.cosTables[3][0];
    auto cos1 = constants.cosTables[3][1];
    b2[0x00] = b1[0x00] + b1[0x03];
    b2[0x03] = (b1[0x00] - b1[0x03]) * cos0;
    b2[0x01] = b1[0x01] + b1[0x02];
    b2[0x02] = (b1[0x01] - b1[0x02]) * cos1;
    b2[0x04] = b1[0x04] + b1[0x07];
    b2[0x07] = (b1[0x07] - b1[0x04]) * cos0;
    b2[0x05] = b1[0x05] + b1[0x06];
    b2[0x06] = (b1[0x06] - b1[0x05]) * cos1;
    b2[0x08] = b1[0x08] + b1[0x0B];
    b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0;
    b2[0x09] = b1[0x09] + b1[0x0A];
    b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1;
    b2[0x0C] = b1[0x0C] + b1[0x0F];
    b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0;
    b2[0x0D] = b1[0x0D] + b1[0x0E];
    b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1;
    b2[0x10] = b1[0x10] + b1[0x13];
    b2[0x13] = (b1[0x10] - b1[0x13]) * cos0;
    b2[0x11] = b1[0x11] + b1[0x12];
    b2[0x12] = (b1[0x11] - b1[0x12]) * cos1;
    b2[0x14] = b1[0x14] + b1[0x17];
    b2[0x17] = (b1[0x17] - b1[0x14]) * cos0;
    b2[0x15] = b1[0x15] + b1[0x16];
    b2[0x16] = (b1[0x16] - b1[0x15]) * cos1;
    b2[0x18] = b1[0x18] + b1[0x1B];
    b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0;
    b2[0x19] = b1[0x19] + b1[0x1A];
    b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1;
    b2[0x1C] = b1[0x1C] + b1[0x1F];
    b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0;
    b2[0x1D] = b1[0x1D] + b1[0x1E];
    b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1;
  }

  {
    auto cos0 = constants.cosTables[4][0];
    b1[0x00] = b2[0x00] + b2[0x01];
    b1[0x01] = (b2[0x00] - b2[0x01]) * cos0;
    b1[0x02] = b2[0x02] + b2[0x03];
    b1[0x03] = (b2[0x03] - b2[0x02]) * cos0;
    b1[0x02] += b1[0x03];
    b1[0x04] = b2[0x04] + b2[0x05];
    b1[0x05] = (b2[0x04] - b2[0x05]) * cos0;
    b1[0x06] = b2[0x06] + b2[0x07];
    b1[0x07] = (b2[0x07] - b2[0x06]) * cos0;
    b1[0x06] += b1[0x07];
    b1[0x04] += b1[0x06];
    b1[0x06] += b1[0x05];
    b1[0x05] += b1[0x07];
    b1[0x08] = b2[0x08] + b2[0x09];
    b1[0x09] = (b2[0x08] - b2[0x09]) * cos0;
    b1[0x0A] = b2[0x0A] + b2[0x0B];
    b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0;
    b1[0x0A] += b1[0x0B];
    b1[0x0C] = b2[0x0C] + b2[0x0D];
    b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0;
    b1[0x0E] = b2[0x0E] + b2[0x0F];
    b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0;
    b1[0x0E] += b1[0x0F];
    b1[0x0C] += b1[0x0E];
    b1[0x0E] += b1[0x0D];
    b1[0x0D] += b1[0x0F];
    b1[0x10] = b2[0x10] + b2[0x11];
    b1[0x11] = (b2[0x10] - b2[0x11]) * cos0;
    b1[0x12] = b2[0x12] + b2[0x13];
    b1[0x13] = (b2[0x13] - b2[0x12]) * cos0;
    b1[0x12] += b1[0x13];
    b1[0x14] = b2[0x14] + b2[0x15];
    b1[0x15] = (b2[0x14] - b2[0x15]) * cos0;
    b1[0x16] = b2[0x16] + b2[0x17];
    b1[0x17] = (b2[0x17] - b2[0x16]) * cos0;
    b1[0x16] += b1[0x17];
    b1[0x14] += b1[0x16];
    b1[0x16] += b1[0x15];
    b1[0x15] += b1[0x17];
    b1[0x18] = b2[0x18] + b2[0x19];
    b1[0x19] = (b2[0x18] - b2[0x19]) * cos0;
    b1[0x1A] = b2[0x1A] + b2[0x1B];
    b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0;
    b1[0x1A] += b1[0x1B];
    b1[0x1C] = b2[0x1C] + b2[0x1D];
    b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0;
    b1[0x1E] = b2[0x1E] + b2[0x1F];
    b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0;
    b1[0x1E] += b1[0x1F];
    b1[0x1C] += b1[0x1E];
    b1[0x1E] += b1[0x1D];
    b1[0x1D] += b1[0x1F];
  }

  out0[0x10 * 16] = b1[0x00];
  out0[0x10 * 12] = b1[0x04];
  out0[0x10 * 8] = b1[0x02];
  out0[0x10 * 4] = b1[0x06];
  out0[0] = b1[0x01];
  out1[0] = b1[0x01];
  out1[0x10 * 4] = b1[0x05];
  out1[0x10 * 8] = b1[0x03];
  out1[0x10 * 12] = b1[0x07];

  b1[0x08] += b1[0x0C];
  out0[0x10 * 14] = b1[0x08];
  b1[0x0C] += b1[0x0a];
  out0[0x10 * 10] = b1[0x0C];
  b1[0x0A] += b1[0x0E];
  out0[0x10 * 6] = b1[0x0A];
  b1[0x0E] += b1[0x09];
  out0[0x10 * 2] = b1[0x0E];
  b1[0x09] += b1[0x0D];
  out1[0x10 * 2] = b1[0x09];
  b1[0x0D] += b1[0x0B];
  out1[0x10 * 6] = b1[0x0D];
  b1[0x0B] += b1[0x0F];
  out1[0x10 * 10] = b1[0x0B];
  out1[0x10 * 14] = b1[0x0F];

  b1[0x18] += b1[0x1C];
  out0[0x10 * 15] = b1[0x10] + b1[0x18];
  out0[0x10 * 13] = b1[0x18] + b1[0x14];
  b1[0x1C] += b1[0x1a];
  out0[0x10 * 11] = b1[0x14] + b1[0x1C];
  out0[0x10 * 9] = b1[0x1C] + b1[0x12];
  b1[0x1A] += b1[0x1E];
  out0[0x10 * 7] = b1[0x12] + b1[0x1A];
  out0[0x10 * 5] = b1[0x1A] + b1[0x16];
  b1[0x1E] += b1[0x19];
  out0[0x10 * 3] = b1[0x16] + b1[0x1E];
  out0[0x10 * 1] = b1[0x1E] + b1[0x11];
  b1[0x19] += b1[0x1D];
  out1[0x10 * 1] = b1[0x11] + b1[0x19];
  out1[0x10 * 3] = b1[0x19] + b1[0x15];
  b1[0x1D] += b1[0x1B];
  out1[0x10 * 5] = b1[0x15] + b1[0x1D];
  out1[0x10 * 7] = b1[0x1D] + b1[0x13];
  b1[0x1B] += b1[0x1F];
  out1[0x10 * 9] = b1[0x13] + b1[0x1B];
  out1[0x10 * 11] = b1[0x1B] + b1[0x17];
  out1[0x10 * 13] = b1[0x17] + b1[0x1F];
  out1[0x10 * 15] = b1[0x1F];
}
} // namespace DCT

//==============================================================================
struct PatchedMP3Stream {
  PatchedMP3Stream(InputStream &source) : stream(source, 8192) { reset(); }

  int decodeNextBlock(float *out0, float *out1, int &done) {
    if (!headerParsed) {
      auto nextFrameOffset = scanForNextFrameHeader(false);

      if (lastFrameSize == -1 || needToSyncBitStream) {
        needToSyncBitStream = false;
        readVBRHeader();

        if (vbrHeaderFound)
          return 1;
      }

      if (nextFrameOffset > 0) {
        wasFreeFormat = false;
        needToSyncBitStream = true;
        auto size =
            (int)(bufferPointer - (bufferSpace[bufferSpaceIndex] + 512));

        if (size > 2880) {
          size = 0;
          bufferPointer = bufferSpace[bufferSpaceIndex] + 512;
        }

        auto toSkip = (size + nextFrameOffset) - 2880;

        if (toSkip > 0) {
          stream.skipNextBytes(toSkip);
          nextFrameOffset -= toSkip;
        }

        stream.read(bufferPointer, nextFrameOffset);
        lastFrameSize += nextFrameOffset;
      }

      uint32 nextIntBigEndian = (uint32)stream.readIntBigEndian();

      static const uint32 LYRICS_HEADER_START = 0x4c595249;
      if (nextFrameOffset < 0) {
        if (nextIntBigEndian == LYRICS_HEADER_START) {
          // Looks like we've hit a Lyrics3 block; a non-standard MP3 extension.
          // Rather than explode, let's just return "we're done" and pretend
          // that this file is over:
          stream.skipNextBytes(stream.getTotalLength() - stream.getPosition());
          return 1;
        } else {
          return -1;
        }
      }

      const auto successful = frame.decodeHeader(nextIntBigEndian);

      if (successful == MP3Frame::ParseSuccessful::no)
        return -1;

      headerParsed = true;
      frameSize = frame.frameSize;
      isFreeFormat = (frameSize == 0);
      sideInfoSize = frame.lsf != 0 ? ((frame.numChannels == 1) ? 9 : 17)
                                    : ((frame.numChannels == 1) ? 17 : 32);

      if (frame.crc16FollowsHeader)
        sideInfoSize += 2;

      bufferSpaceIndex = 1 - bufferSpaceIndex;
      bufferPointer = bufferSpace[bufferSpaceIndex] + 512;
      bitIndex = 0;

      if (lastFrameSize < 0)
        return 1;
    }

    if (!sideParsed) {
      if (frame.layer == 3) {
        stream.read(bufferPointer, sideInfoSize);

        if (frame.crc16FollowsHeader)
          getBits(16);

        auto bits = jmax(0, decodeLayer3SideInfo());
        dataSize = (bits + 7) / 8;

        if (!isFreeFormat)
          dataSize = jmin(dataSize, frame.frameSize - sideInfoSize);
      } else {
        dataSize = frame.frameSize;
        sideInfoSize = 0;
      }

      sideParsed = true;
    }

    int result = 1;

    if (!dataParsed) {
      stream.read(bufferPointer, dataSize);

      if (out0 != nullptr) {
        if (frame.layer < 3 && frame.crc16FollowsHeader)
          getBits(16);

        switch (frame.layer) {
        case 1:
          decodeLayer1Frame(out0, out1, done);
          break;
        case 2:
          decodeLayer2Frame(out0, out1, done);
          break;
        case 3:
          decodeLayer3Frame(out0, out1, done);
          break;
        default:
          break;
        }
      }

      bufferPointer =
          bufferSpace[bufferSpaceIndex] + 512 + sideInfoSize + dataSize;
      dataParsed = true;
      result = 0;

      // We decoded a frame correctly, so increase the amount of patience
      // we have when scanning for the next frame header:
      if (maximumScanLength < 0xFFFFFFF) {
        maximumScanLength *= 2;
      }
    }

    if (isFreeFormat) {
      if (wasFreeFormat) {
        frameSize = lastFrameSizeNoPadding + frame.padding;
      } else {
        auto nextFrameOffset = scanForNextFrameHeader(true);

        wasFreeFormat = isFreeFormat;

        if (nextFrameOffset < 0) {
          lastFrameSize = frameSize;
          return result;
        }

        frameSize = nextFrameOffset + sideInfoSize + dataSize;
        lastFrameSizeNoPadding = frameSize - frame.padding;
      }
    }

    if (result == 0)
      return result;

    int bytes = frameSize - (sideInfoSize + dataSize);

    if (bytes > 0) {
      auto toSkip = bytes - 512;

      if (toSkip > 0) {
        stream.skipNextBytes(toSkip);
        bytes -= toSkip;
        frameSize -= toSkip;
      }

      stream.read(bufferPointer, bytes);
      bufferPointer += bytes;
    }

    lastFrameSize = frameSize;
    wasFreeFormat = isFreeFormat;
    frameSize = 0;
    headerParsed = sideParsed = dataParsed = false;
    return result;
  }

  bool seek(int frameIndex) {
    frameIndex = jmax(0, frameIndex);

    while (frameIndex >= frameStreamPositions.size() * storedStartPosInterval) {
      int dummy = 0;
      auto result = decodeNextBlock(nullptr, nullptr, dummy);

      if (result < 0)
        return false;

      if (result > 0)
        break;
    }

    frameIndex =
        jmin(frameIndex & ~(storedStartPosInterval - 1),
             (frameStreamPositions.size() - 1) * storedStartPosInterval);

    stream.setPosition(
        frameStreamPositions.getUnchecked(frameIndex / storedStartPosInterval));
    currentFrameIndex = frameIndex;
    reset();
    return true;
  }

  MP3Frame frame;
  VBRTagData vbrTagData;
  BufferedInputStream stream;
  int numFrames = 0, currentFrameIndex = 0;
  bool vbrHeaderFound = false;

private:
  bool headerParsed, sideParsed, dataParsed, needToSyncBitStream;
  bool isFreeFormat, wasFreeFormat;
  int sideInfoSize, dataSize;
  int frameSize, lastFrameSize, lastFrameSizeNoPadding;
  int bufferSpaceIndex;
  Layer3SideInfo sideinfo;
  uint8 bufferSpace[2][2880 + 1024];
  uint8 *bufferPointer;
  int bitIndex, synthBo;
  float hybridBlock[2][2][32 * 18];
  int hybridBlockIndex[2];
  float synthBuffers[2][2][0x110];
  float hybridIn[2][32][18];
  float hybridOut[2][18][32];
  unsigned int maximumScanLength = 1024;

  void reset() noexcept {
    headerParsed = sideParsed = dataParsed = isFreeFormat = wasFreeFormat =
        false;
    lastFrameSize = -1;
    needToSyncBitStream = true;
    frameSize = sideInfoSize = dataSize = bitIndex = 0;
    lastFrameSizeNoPadding = bufferSpaceIndex = 0;
    bufferPointer = bufferSpace[bufferSpaceIndex] + 512;
    synthBo = 1;
    maximumScanLength = 1024;

    zerostruct(sideinfo);
    zeromem(bufferSpace, sizeof(bufferSpace));
    zeromem(hybridBlock, sizeof(hybridBlock));
    zeromem(hybridBlockIndex, sizeof(hybridBlockIndex));
    zeromem(synthBuffers, sizeof(synthBuffers));
  }

  enum { storedStartPosInterval = 4 };
  Array<int64> frameStreamPositions;

  struct SideInfoLayer1 {
    uint8 allocation[32][2];
    uint8 scaleFactor[32][2];
  };

  struct SideInfoLayer2 {
    uint8 allocation[32][2];
    uint8 scaleFactor[32][2][3];
  };

  static bool isValidHeader(uint32 header, int oldLayer) noexcept {
    auto newLayer = (int)(4 - ((header >> 17) & 3));

    return (header & 0xffe00000) == 0xffe00000 && newLayer != 4 &&
           (oldLayer <= 0 || newLayer == oldLayer) &&
           ((header >> 12) & 15) != 15 && ((header >> 10) & 3) != 3 &&
           (header & 3) != 2;
  }

  bool rollBackBufferPointer(int backstep) noexcept {
    if (lastFrameSize < 0 && backstep > 0)
      return false;

    auto *oldBuffer = bufferSpace[1 - bufferSpaceIndex] + 512;
    bufferPointer -= backstep;

    if (backstep != 0)
      memcpy(bufferPointer, oldBuffer + lastFrameSize - backstep,
             (size_t)backstep);

    bitIndex = 0;
    return true;
  }

  uint32 getBits(int numBits) noexcept {
    if (numBits <= 0 || bufferPointer == nullptr)
      return 0;

    const auto result =
        (uint32)(((((((bufferPointer[0] << 8) | bufferPointer[1]) << 8) |
                    bufferPointer[2])
                   << bitIndex) &
                  0xffffff) >>
                 (24 - numBits));
    bitIndex += numBits;
    bufferPointer += (bitIndex >> 3);
    bitIndex &= 7;
    return result;
  }

  uint32 getOneBit() noexcept {
    auto result = (uint8)(*bufferPointer << bitIndex);
    ++bitIndex;
    bufferPointer += (bitIndex >> 3);
    bitIndex &= 7;
    return (uint32)(result >> 7);
  }

  uint32 getBitsUnchecked(int numBits) noexcept {
    const auto result =
        (uint32)(((((bufferPointer[0] << 8) | bufferPointer[1]) << bitIndex) &
                  0xffff) >>
                 (16 - numBits));
    bitIndex += numBits;
    bufferPointer += (bitIndex >> 3);
    bitIndex &= 7;
    return result;
  }

  inline uint8 getBitsUint8(int numBits) noexcept {
    return (uint8)getBitsUnchecked(numBits);
  }
  inline uint16 getBitsUint16(int numBits) noexcept {
    return (uint16)getBitsUnchecked(numBits);
  }

  int scanForNextFrameHeader(bool checkTypeAgainstLastFrame) noexcept {
    auto oldPos = stream.getPosition();
    int offset = -3;
    uint32 header = 0;
    bool isParsingFirstFrame = oldPos == 0;

    for (;;) {
      auto streamPos = stream.getPosition();

      if (stream.isExhausted() || streamPos > oldPos + 32768) {
        offset = -1;
        break;
      }

      header = (header << 8) | (uint8)stream.readByte();

      if (offset >= 0 && isValidHeader(header, frame.layer)) {
        if (!checkTypeAgainstLastFrame)
          break;

        const bool mpeg25 = (header & (1 << 20)) == 0;
        const uint32 lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1);
        const uint32 sampleRateIndex = mpeg25
                                           ? (6 + ((header >> 10) & 3))
                                           : (((header >> 10) & 3) + (lsf * 3));
        const uint32 mode = (header >> 6) & 3;
        const uint32 numChannels = (mode == 3) ? 1 : 2;

        if (numChannels == (uint32)frame.numChannels &&
            lsf == (uint32)frame.lsf && mpeg25 == frame.mpeg25 &&
            sampleRateIndex == (uint32)frame.sampleRateIndex)
          break;
      }

      if (isParsingFirstFrame && offset == 0) {
        // If we're parsing the first frame of a file/stream, that frame must be
        // at the start of the stream (i.e.: the first four bytes must be a
        // valid MP3 header).
        //
        // If the first four bytes were a valid MP3 header, the above `if (...
        // isValidHeader)` check would have exited the `for` loop, and we'd
        // never get here.
        //
        // This prevents accidentally parsing .mp4 files (among others) as MP3
        // files that start with junk.
        offset = -1;
        break;
      }

      ++offset;
    }

    if (offset >= 0) {
      if ((currentFrameIndex & (storedStartPosInterval - 1)) == 0)
        frameStreamPositions.set(currentFrameIndex / storedStartPosInterval,
                                 oldPos + offset);

      ++currentFrameIndex;
    }

    stream.setPosition(oldPos);
    return offset;
  }

  void readVBRHeader() {
    auto oldPos = stream.getPosition();
    uint8 xing[194];
    stream.read(xing, sizeof(xing));

    vbrHeaderFound = vbrTagData.read(xing);

    if (vbrHeaderFound) {
      numFrames = (int)vbrTagData.frames;
      oldPos += jmax(vbrTagData.headersize, 1);
    }

    stream.setPosition(oldPos);
  }

  void decodeLayer1Frame(float *pcm0, float *pcm1, int &samplesDone) noexcept {
    float fraction[2][32];
    SideInfoLayer1 si;
    layer1Step1(si);
    auto single =
        (frame.numChannels == 1 || frame.single == 3) ? 0 : frame.single;

    if (single >= 0) {
      for (int i = 0; i < 12; ++i) {
        layer1Step2(si, fraction);
        synthesise(fraction[single], 0, pcm0, samplesDone);
      }
    } else {
      for (int i = 0; i < 12; ++i) {
        layer1Step2(si, fraction);
        synthesiseStereo(fraction[0], fraction[1], pcm0, pcm1, samplesDone);
      }
    }
  }

  void decodeLayer2Frame(float *pcm0, float *pcm1, int &samplesDone) {
    float fraction[2][4][32];
    frame.selectLayer2Table();
    SideInfoLayer2 si;
    layer2Step1(si);
    auto single =
        (frame.numChannels == 1 || frame.single == 3) ? 0 : frame.single;

    if (single >= 0) {
      for (int i = 0; i < 12; ++i) {
        layer2Step2(si, i >> 2, fraction);

        for (int j = 0; j < 3; ++j)
          synthesise(fraction[single][j], 0, pcm0, samplesDone);
      }
    } else {
      for (int i = 0; i < 12; ++i) {
        layer2Step2(si, i >> 2, fraction);

        for (int j = 0; j < 3; ++j)
          synthesiseStereo(fraction[0][j], fraction[1][j], pcm0, pcm1,
                           samplesDone);
      }
    }
  }

  void decodeLayer3Frame(float *pcm0, float *pcm1, int &samplesDone) noexcept {
    if (!rollBackBufferPointer((int)sideinfo.mainDataStart))
      return;

    const int single = frame.numChannels == 1 ? 0 : frame.single;
    const int numChans = (frame.numChannels == 1 || single >= 0) ? 1 : 2;
    const bool msStereo = (frame.mode == 1) && (frame.modeExt & 2) != 0;
    const bool iStereo = (frame.mode == 1) && (frame.modeExt & 1) != 0;
    const int granules = frame.lsf ? 1 : 2;
    int scaleFactors[2][39];

    for (int gr = 0; gr < granules; ++gr) {
      {
        auto &granule = sideinfo.ch[0].gr[gr];
        auto part2bits =
            frame.lsf ? getLayer3ScaleFactors2(scaleFactors[0], granule, 0)
                      : getLayer3ScaleFactors1(scaleFactors[0], granule);

        if (layer3DequantizeSample(hybridIn[0], scaleFactors[0], granule,
                                   frame.sampleRateIndex, part2bits))
          return;
      }

      if (frame.numChannels == 2) {
        auto &granule = sideinfo.ch[1].gr[gr];
        auto part2bits =
            frame.lsf
                ? getLayer3ScaleFactors2(scaleFactors[1], granule, iStereo)
                : getLayer3ScaleFactors1(scaleFactors[1], granule);

        if (layer3DequantizeSample(hybridIn[1], scaleFactors[1], granule,
                                   frame.sampleRateIndex, part2bits))
          return;

        if (msStereo) {
          for (int i = 0; i < 32 * 18; ++i) {
            auto tmp0 = ((const float *)hybridIn[0])[i];
            auto tmp1 = ((const float *)hybridIn[1])[i];
            ((float *)hybridIn[1])[i] = tmp0 - tmp1;
            ((float *)hybridIn[0])[i] = tmp0 + tmp1;
          }
        }

        if (iStereo)
          granule.doIStereo(hybridIn, scaleFactors[1], frame.sampleRateIndex,
                            msStereo, frame.lsf);

        if (msStereo || iStereo || single == 3) {
          if (granule.maxb > sideinfo.ch[0].gr[gr].maxb)
            sideinfo.ch[0].gr[gr].maxb = granule.maxb;
          else
            granule.maxb = sideinfo.ch[0].gr[gr].maxb;
        }

        switch (single) {
        case 3: {
          auto *in0 = (float *)hybridIn[0];
          auto *in1 = (const float *)hybridIn[1];

          for (int i = 0; i < (int)(18 * granule.maxb); ++i, ++in0)
            *in0 = (*in0 + *in1++);
        } break;

        case 1: {
          auto *in0 = (float *)hybridIn[0];
          auto *in1 = (const float *)hybridIn[1];

          for (int i = 0; i < (int)(18 * granule.maxb); ++i)
            *in0++ = *in1++;
        } break;

        default:
          break;
        }
      }

      for (int ch = 0; ch < numChans; ++ch) {
        auto &granule = sideinfo.ch[ch].gr[gr];
        granule.doAntialias(hybridIn[ch]);
        layer3Hybrid(hybridIn[ch], hybridOut[ch], ch, granule);
      }

      for (int ss = 0; ss < 18; ++ss) {
        if (single >= 0)
          synthesise(hybridOut[0][ss], 0, pcm0, samplesDone);
        else
          synthesiseStereo(hybridOut[0][ss], hybridOut[1][ss], pcm0, pcm1,
                           samplesDone);
      }
    }
  }

  int decodeLayer3SideInfo() noexcept {
    const int numChannels = frame.numChannels;
    const int sampleRate = frame.sampleRateIndex;
    const int single = (numChannels == 1) ? 0 : frame.single;
    const bool msStereo = (frame.mode == 1) && (frame.modeExt & 2) != 0;
    const int granules = frame.lsf ? 1 : 2;

    if (frame.lsf == 0)
      getLayer3SideInfo1(numChannels, msStereo, sampleRate, single);
    else
      getLayer3SideInfo2(numChannels, msStereo, sampleRate, single);

    int databits = 0;

    for (int gr = 0; gr < granules; ++gr)
      for (int ch = 0; ch < numChannels; ++ch)
        databits += (int)sideinfo.ch[ch].gr[gr].part2_3Length;

    return databits - 8 * (int)sideinfo.mainDataStart;
  }

  void layer1Step1(SideInfoLayer1 &si) noexcept {
    zerostruct(si);
    int i, jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : 32;

    if (frame.numChannels == 2) {
      for (i = 0; i < jsbound; ++i) {
        si.allocation[i][0] = getBitsUint8(4);
        si.allocation[i][1] = getBitsUint8(4);
      }

      for (i = jsbound; i < 32; ++i)
        si.allocation[i][0] = si.allocation[i][1] = getBitsUint8(4);

      for (i = 0; i < 32; ++i) {
        si.scaleFactor[i][0] = si.allocation[i][0] ? getBitsUint8(6) : 0;
        si.scaleFactor[i][1] = si.allocation[i][1] ? getBitsUint8(6) : 0;
      }
    } else {
      for (i = 0; i < 32; ++i)
        si.allocation[i][0] = getBitsUint8(4);

      for (i = 0; i < 32; ++i)
        si.scaleFactor[i][0] = si.allocation[i][0] ? getBitsUint8(6) : 0;
    }
  }

  void layer1Step2(SideInfoLayer1 &si, float fraction[2][32]) noexcept {
    if (frame.numChannels == 2) {
      int i, jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : 32;

      for (i = 0; i < jsbound; ++i) {
        const uint8 n0 = si.allocation[i][0];
        const uint8 n1 = si.allocation[i][1];
        fraction[0][i] =
            n0 > 0 ? ((float)(-(1 << n0) + getBitsUint16(n0 + 1) + 1) *
                      constants.muls[n0 + 1][si.scaleFactor[i][0]])
                   : 0.0f;
        fraction[1][i] =
            n1 > 0 ? ((float)(-(1 << n1) + getBitsUint16(n1 + 1) + 1) *
                      constants.muls[n1 + 1][si.scaleFactor[i][1]])
                   : 0.0f;
      }

      for (i = jsbound; i < 32; ++i) {
        const uint8 n = si.allocation[i][0];

        if (n > 0) {
          const uint32 w = ((uint32) - (1 << n) + getBitsUint16(n + 1) + 1);
          fraction[0][i] =
              ((float)w * constants.muls[n + 1][si.scaleFactor[i][0]]);
          fraction[1][i] =
              ((float)w * constants.muls[n + 1][si.scaleFactor[i][1]]);
        } else
          fraction[0][i] = fraction[1][i] = 0;
      }
    } else {
      for (int i = 0; i < 32; ++i) {
        const uint8 n = si.allocation[i][0];
        const uint8 j = si.scaleFactor[i][0];

        if (n > 0)
          fraction[0][i] = ((float)(-(1 << n) + getBitsUint16(n + 1) + 1) *
                            constants.muls[n + 1][j]);
        else
          fraction[0][i] = 0;
      }
    }
  }

  void layer2Step1(SideInfoLayer2 &si) noexcept {
    zerostruct(si);
    const auto sblimit = frame.layer2SubBandLimit;
    const auto jsbound =
        (frame.mode == 1 ? jmin((frame.modeExt << 2) + 4, sblimit) : sblimit);
    auto *allocTable = frame.allocationTable;
    uint8 scfsi[32][2];

    if (frame.numChannels == 2) {
      for (int i = 0; i < jsbound; ++i) {
        auto step = allocTable->bits;
        allocTable += (static_cast<intptr_t>(1) << step);
        si.allocation[i][0] = getBitsUint8(step);
        si.allocation[i][1] = getBitsUint8(step);
      }

      for (int i = jsbound; i < sblimit; ++i) {
        auto step = allocTable->bits;
        auto b0 = getBitsUint8(step);
        allocTable += (static_cast<intptr_t>(1) << step);
        si.allocation[i][0] = b0;
        si.allocation[i][1] = b0;
      }

      for (int i = 0; i < sblimit; ++i) {
        scfsi[i][0] = si.allocation[i][0] ? getBitsUint8(2) : 0;
        scfsi[i][1] = si.allocation[i][1] ? getBitsUint8(2) : 0;
      }
    } else {
      for (int i = 0; i < sblimit; ++i) {
        const int16 step = allocTable->bits;
        allocTable += (static_cast<intptr_t>(1) << step);
        si.allocation[i][0] = getBitsUint8(step);
      }

      for (int i = 0; i < sblimit; ++i)
        scfsi[i][0] = si.allocation[i][0] ? getBitsUint8(2) : 0;
    }

    for (int i = 0; i < sblimit; ++i) {
      for (int ch = 0; ch < frame.numChannels; ++ch) {
        uint8 s0 = 0, s1 = 0, s2 = 0;

        if (si.allocation[i][ch]) {
          switch (scfsi[i][ch]) {
          case 0:
            s0 = getBitsUint8(6);
            s1 = getBitsUint8(6);
            s2 = getBitsUint8(6);
            break;
          case 1:
            s1 = s0 = getBitsUint8(6);
            s2 = getBitsUint8(6);
            break;
          case 2:
            s2 = s1 = s0 = getBitsUint8(6);
            break;
          case 3:
            s0 = getBitsUint8(6);
            s2 = s1 = getBitsUint8(6);
            break;
          default:
            break;
          }
        }

        si.scaleFactor[i][ch][0] = s0;
        si.scaleFactor[i][ch][1] = s1;
        si.scaleFactor[i][ch][2] = s2;
      }
    }
  }

  void layer2Step2(SideInfoLayer2 &si, const int gr,
                   float fraction[2][4][32]) noexcept {
    auto *allocTable = frame.allocationTable;
    auto sblimit = frame.layer2SubBandLimit;
    const auto jsbound =
        (frame.mode == 1 ? jmin((frame.modeExt << 2) + 4, sblimit) : sblimit);

    for (int i = 0; i < jsbound; ++i) {
      auto step = allocTable->bits;

      for (int ch = 0; ch < frame.numChannels; ++ch) {
        if (auto ba = si.allocation[i][ch]) {
          auto x1 = jmin((uint8)63, si.scaleFactor[i][ch][gr]);
          auto *alloc2 = allocTable + ba;
          auto k = jmin((int16)16, alloc2->bits);
          auto d1 = alloc2->d;

          if (d1 < 0) {
            const double cm = constants.muls[k][x1];
            fraction[ch][0][i] = (float)(((int)getBits(k) + d1) * cm);
            fraction[ch][1][i] = (float)(((int)getBits(k) + d1) * cm);
            fraction[ch][2][i] = (float)(((int)getBits(k) + d1) * cm);
          } else {
            auto *tab = constants.getGroupTable(d1, getBits(k));
            fraction[ch][0][i] = (float)constants.muls[tab[0]][x1];
            fraction[ch][1][i] = (float)constants.muls[tab[1]][x1];
            fraction[ch][2][i] = (float)constants.muls[tab[2]][x1];
          }
        } else {
          fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0;
        }
      }

      allocTable += (static_cast<intptr_t>(1) << step);
    }

    for (int i = jsbound; i < frame.layer2SubBandLimit; ++i) {
      auto step = allocTable->bits;
      auto ba = si.allocation[i][0];

      if (ba != 0) {
        auto *alloc2 = allocTable + ba;
        int16 k = alloc2->bits;
        int16 d1 = alloc2->d;
        k = (k <= 16) ? k : 16;

        if (d1 < 0) {
          auto v0 = (int)getBits(k);
          auto v1 = (int)getBits(k);
          auto v2 = (int)getBits(k);

          for (int ch = 0; ch < frame.numChannels; ++ch) {
            auto x1 = jmin((uint8)63, si.scaleFactor[i][ch][gr]);
            const double cm = constants.muls[k][x1];
            fraction[ch][0][i] = (float)((v0 + d1) * cm);
            fraction[ch][1][i] = (float)((v1 + d1) * cm);
            fraction[ch][2][i] = (float)((v2 + d1) * cm);
          }
        } else {
          auto *tab = constants.getGroupTable(d1, getBits(k));
          auto k0 = tab[0];
          auto k1 = tab[1];
          auto k2 = tab[2];

          for (int ch = 0; ch < frame.numChannels; ++ch) {
            auto x1 = jmin((uint8)63, si.scaleFactor[i][ch][gr]);
            fraction[ch][0][i] = (float)constants.muls[k0][x1];
            fraction[ch][1][i] = (float)constants.muls[k1][x1];
            fraction[ch][2][i] = (float)constants.muls[k2][x1];
          }
        }
      } else {
        fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] = 0;
        fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0;
      }

      allocTable += (static_cast<intptr_t>(1) << step);
    }

    for (int ch = 0; ch < frame.numChannels; ++ch)
      for (int i = frame.layer2SubBandLimit; i < 32; ++i)
        fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0;
  }

  void getLayer3SideInfo1(const int stereo, const bool msStereo,
                          const int sampleRate, const int single) noexcept {
    const int powdiff = (single == 3) ? 4 : 0;
    sideinfo.mainDataStart = getBits(9);
    sideinfo.privateBits = getBitsUnchecked(stereo == 1 ? 5 : 3);

    for (int ch = 0; ch < stereo; ++ch) {
      sideinfo.ch[ch].gr[0].scfsi = -1;
      sideinfo.ch[ch].gr[1].scfsi = (int)getBitsUnchecked(4);
    }

    for (int gr = 0; gr < 2; ++gr) {
      for (int ch = 0; ch < stereo; ++ch) {
        auto &granule = sideinfo.ch[ch].gr[gr];

        granule.part2_3Length = getBits(12);
        granule.bigValues = jmin(288u, getBitsUnchecked(9));

        const int qss = (int)getBitsUnchecked(8);
        granule.pow2gain = constants.powToGains + 256 - qss + powdiff;

        if (msStereo)
          granule.pow2gain += 2;

        granule.scaleFactorCompression = getBitsUnchecked(4);

        if (getOneBit()) {
          granule.blockType = getBitsUnchecked(2);
          granule.mixedBlockFlag = getOneBit();
          granule.tableSelect[0] = getBitsUnchecked(5);
          granule.tableSelect[1] = getBitsUnchecked(5);
          granule.tableSelect[2] = 0;

          for (int i = 0; i < 3; ++i) {
            const uint32 sbg = (getBitsUnchecked(3) << 3);
            granule.fullGain[i] = granule.pow2gain + sbg;
          }

          granule.region1Start = 36 >> 1;
          granule.region2Start = 576 >> 1;
        } else {
          for (int i = 0; i < 3; ++i)
            granule.tableSelect[i] = getBitsUnchecked(5);

          const int r0c = (int)getBitsUnchecked(4);
          const int r1c = (int)getBitsUnchecked(3);
          const int region0index = jmin(22, r0c + 1);
          const int region1index = jmin(22, r0c + 1 + r1c + 1);

          granule.region1Start =
              (uint32)(bandInfo[sampleRate].longIndex[region0index] >> 1);
          granule.region2Start =
              (uint32)(bandInfo[sampleRate].longIndex[region1index] >> 1);
          granule.blockType = 0;
          granule.mixedBlockFlag = 0;
        }

        granule.preflag = getOneBit();
        granule.scaleFactorScale = getOneBit();
        granule.count1TableSelect = getOneBit();
      }
    }
  }

  void getLayer3SideInfo2(const int stereo, const bool msStereo,
                          const int sampleRate, const int single) noexcept {
    const int powdiff = (single == 3) ? 4 : 0;
    sideinfo.mainDataStart = getBits(8);
    sideinfo.privateBits = stereo == 1 ? getOneBit() : getBitsUnchecked(2);

    for (int ch = 0; ch < stereo; ++ch) {
      auto &granule = sideinfo.ch[ch].gr[0];

      granule.part2_3Length = getBits(12);
      granule.bigValues = jmin(288u, getBitsUnchecked(9));

      const uint32 qss = getBitsUnchecked(8);
      granule.pow2gain = constants.powToGains + 256 - qss + powdiff;

      if (msStereo)
        granule.pow2gain += 2;

      granule.scaleFactorCompression = getBits(9);

      if (getOneBit()) {
        granule.blockType = getBitsUnchecked(2);
        granule.mixedBlockFlag = getOneBit();
        granule.tableSelect[0] = getBitsUnchecked(5);
        granule.tableSelect[1] = getBitsUnchecked(5);
        granule.tableSelect[2] = 0;

        for (int i = 0; i < 3; ++i) {
          const uint32 sbg = (getBitsUnchecked(3) << 3);
          granule.fullGain[i] = granule.pow2gain + sbg;
        }

        if (granule.blockType == 0) {
        }

        if (granule.blockType == 2)
          granule.region1Start = sampleRate == 8 ? 36 : (36 >> 1);
        else
          granule.region1Start = sampleRate == 8 ? (108 >> 1) : (54 >> 1);

        granule.region2Start = 576 >> 1;
      } else {
        for (int i = 0; i < 3; ++i)
          granule.tableSelect[i] = getBitsUnchecked(5);

        const int r0c = (int)getBitsUnchecked(4);
        const int r1c = (int)getBitsUnchecked(3);
        const int region0index = jmin(22, r0c + 1);
        const int region1index = jmin(22, r0c + 1 + r1c + 1);

        granule.region1Start =
            (uint32)(bandInfo[sampleRate].longIndex[region0index] >> 1);
        granule.region2Start =
            (uint32)(bandInfo[sampleRate].longIndex[region1index] >> 1);
        granule.blockType = 0;
        granule.mixedBlockFlag = 0;
      }
      granule.scaleFactorScale = getOneBit();
      granule.count1TableSelect = getOneBit();
    }
  }

  int getLayer3ScaleFactors1(int *scf,
                             const Layer3SideInfo::Info &granule) noexcept {
    static const uint8 lengths[2][16] = {
        {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4},
        {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}};

    int numBits;
    const int num0 = lengths[0][granule.scaleFactorCompression];
    const int num1 = lengths[1][granule.scaleFactorCompression];

    if (granule.blockType == 2) {
      int i = 18;
      numBits = (num0 + num1) * 18;

      if (granule.mixedBlockFlag) {
        for (int j = 8; --j >= 0;)
          *scf++ = (int)getBitsUnchecked(num0);
        numBits -= num0;
        i = 9;
      }

      for (; --i >= 0;)
        *scf++ = (int)getBitsUnchecked(num0);
      for (i = 18; --i >= 0;)
        *scf++ = (int)getBitsUnchecked(num1);

      *scf++ = 0;
      *scf++ = 0;
      *scf++ = 0;
    } else {
      const int scfsi = granule.scfsi;

      if (scfsi < 0) {
        for (int i = 11; --i >= 0;)
          *scf++ = (int)getBitsUnchecked(num0);
        for (int j = 10; --j >= 0;)
          *scf++ = (int)getBitsUnchecked(num1);
        numBits = (num0 + num1) * 10 + num0;
      } else {
        numBits = 0;
        if ((scfsi & 8) == 0) {
          for (int i = 6; --i >= 0;)
            *scf++ = (int)getBitsUnchecked(num0);
          numBits += num0 * 6;
        } else
          scf += 6;

        if ((scfsi & 4) == 0) {
          for (int i = 5; --i >= 0;)
            *scf++ = (int)getBitsUnchecked(num0);
          numBits += num0 * 5;
        } else
          scf += 5;

        if ((scfsi & 2) == 0) {
          for (int i = 5; --i >= 0;)
            *scf++ = (int)getBitsUnchecked(num1);
          numBits += num1 * 5;
        } else
          scf += 5;

        if ((scfsi & 1) == 0) {
          for (int i = 5; --i >= 0;)
            *scf++ = (int)getBitsUnchecked(num1);
          numBits += num1 * 5;
        } else
          scf += 5;
      }

      *scf = 0;
    }

    return numBits;
  }

  JUCE_BEGIN_IGNORE_WARNINGS_MSVC(6385)
  int getLayer3ScaleFactors2(int *scf, Layer3SideInfo::Info &granule,
                             const bool iStereo) noexcept {
    static const uint8 scaleTable[3][6][4] = {{{6, 5, 5, 5},
                                               {6, 5, 7, 3},
                                               {11, 10, 0, 0},
                                               {7, 7, 7, 0},
                                               {6, 6, 6, 3},
                                               {8, 8, 5, 0}},
                                              {{9, 9, 9, 9},
                                               {9, 9, 12, 6},
                                               {18, 18, 0, 0},
                                               {12, 12, 12, 0},
                                               {12, 9, 9, 6},
                                               {15, 12, 9, 0}},
                                              {{6, 9, 9, 9},
                                               {6, 9, 12, 6},
                                               {15, 18, 0, 0},
                                               {6, 15, 12, 0},
                                               {6, 12, 9, 6},
                                               {6, 18, 9, 0}}};

    uint32 len = iStereo
                     ? constants.iLength2[granule.scaleFactorCompression >> 1]
                     : constants.nLength2[granule.scaleFactorCompression];

    granule.preflag = (len >> 15) & 1;

    int n = 0;
    if (granule.blockType == 2) {
      ++n;
      if (granule.mixedBlockFlag)
        ++n;
    }

    const uint8 *const data = scaleTable[n][(len >> 12) & 7];
    int numBits = 0;

    for (int i = 0; i < 4; ++i) {
      int num = len & 7;
      len >>= 3;

      if (num) {
        for (int j = 0; j < (int)(data[i]); ++j)
          *scf++ = (int)getBitsUnchecked(num);

        numBits += data[i] * num;
      } else {
        for (int j = 0; j < (int)(data[i]); ++j)
          *scf++ = 0;
      }
    }

    n = (n << 1) + 1;

    for (int i = 0; i < n; ++i)
      *scf++ = 0;

    return numBits;
  }
  JUCE_END_IGNORE_WARNINGS_MSVC

  bool layer3DequantizeSample(float xr[32][18], int *scf,
                              Layer3SideInfo::Info &granule, int sampleRate,
                              int part2bits) noexcept {
    const uint32 shift = 1 + granule.scaleFactorScale;
    auto *xrpnt = (float *)xr;
    auto part2remain = (int)granule.part2_3Length - part2bits;

    zeromem(xrpnt, (size_t)(&xr[32][0] - xrpnt) * sizeof(float));

    auto bv = (int)granule.bigValues;
    auto region1 = (int)granule.region1Start;
    auto region2 = (int)granule.region2Start;
    int l3 = ((576 >> 1) - bv) >> 1;
    int l[3];

    if (bv <= region1) {
      l[0] = bv;
      l[1] = 0;
      l[2] = 0;
    } else {
      l[0] = region1;
      if (bv <= region2) {
        l[1] = bv - l[0];
        l[2] = 0;
      } else {
        l[1] = region2 - l[0];
        l[2] = bv - region2;
      }
    }

    for (int i = 0; i < 3; ++i)
      if (l[i] < 0)
        l[i] = 0;

    if (granule.blockType == 2) {
      int max[4];
      int step = 0, lwin = 0, cb = 0, mc = 0;
      float v = 0;
      int *map;
      int *mapEnd;

      if (granule.mixedBlockFlag) {
        max[3] = -1;
        max[0] = max[1] = max[2] = 2;
        map = constants.map[sampleRate][0];
        mapEnd = constants.mapEnd[sampleRate][0];
      } else {
        max[0] = max[1] = max[2] = max[3] = -1;
        map = constants.map[sampleRate][1];
        mapEnd = constants.mapEnd[sampleRate][1];
      }

      for (int i = 0; i < 2; ++i) {
        auto *h = huffmanTables1 + granule.tableSelect[i];

        for (int lp = l[i]; lp != 0; --lp, --mc) {
          int x, y;
          if (mc == 0) {
            mc = *map++;
            xrpnt = ((float *)xr) + (*map++);
            lwin = *map++;
            cb = *map++;

            if (lwin == 3) {
              v = granule.pow2gain[(*scf++) << shift];
              step = 1;
            } else {
              v = granule.fullGain[lwin][(*scf++) << shift];
              step = 3;
            }
          }

          auto *val = h->table;

          while ((y = *val++) < 0) {
            if (getOneBit())
              val -= y;

            --part2remain;
          }

          x = y >> 4;
          y &= 15;

          if (x == 15) {
            max[lwin] = cb;
            part2remain -= (int)(h->bits + 1);
            x += (int)getBits((int)h->bits);
            *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v);
          } else if (x) {
            max[lwin] = cb;
            *xrpnt = constants.nToThe4Over3[x] * (getOneBit() ? -v : v);
            --part2remain;
          } else
            *xrpnt = 0;

          xrpnt += step;

          if (y == 15) {
            max[lwin] = cb;
            part2remain -= (int)(h->bits + 1);
            y += (int)getBits((int)h->bits);
            *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v);
          } else if (y) {
            max[lwin] = cb;
            *xrpnt = constants.nToThe4Over3[y] * (getOneBit() ? -v : v);
            --part2remain;
          } else
            *xrpnt = 0;

          xrpnt += step;
        }
      }

      for (; l3 && (part2remain > 0); --l3) {
        auto *h = huffmanTables2 + granule.count1TableSelect;
        auto *val = h->table;
        int16 a;

        while ((a = *val++) < 0) {
          if (part2remain <= 0) {
            a = 0;
            break;
          }

          --part2remain;

          if (getOneBit())
            val -= a;
        }

        for (int i = 0; i < 4; ++i) {
          if ((i & 1) == 0) {
            if (mc == 0) {
              mc = *map++;
              xrpnt = ((float *)xr) + (*map++);
              lwin = *map++;
              cb = *map++;

              if (lwin == 3) {
                v = granule.pow2gain[(*scf++) << shift];
                step = 1;
              } else {
                v = granule.fullGain[lwin][(*scf++) << shift];
                step = 3;
              }
            }

            --mc;
          }

          if ((a & (8 >> i))) {
            max[lwin] = cb;

            if (part2remain == 0)
              break;

            --part2remain;
            *xrpnt = getOneBit() ? -v : v;
          } else
            *xrpnt = 0;

          xrpnt += step;
        }
      }

      while (map < mapEnd) {
        if (mc == 0) {
          mc = *map++;
          xrpnt = ((float *)xr) + *map++;
          step = (*map++ == 3) ? 1 : 3;
          ++map;
        }

        --mc;
        *xrpnt = 0;
        xrpnt += step;
        *xrpnt = 0;
        xrpnt += step;
      }

      granule.maxBand[0] = (uint32)(max[0] + 1);
      granule.maxBand[1] = (uint32)(max[1] + 1);
      granule.maxBand[2] = (uint32)(max[2] + 1);
      granule.maxBandl = (uint32)(max[3] + 1);

      const int rmax = jmax(max[0], max[1], max[3]) + 1;
      granule.maxb = rmax ? (uint32)constants.shortLimit[sampleRate][rmax]
                          : (uint32)constants.longLimit[sampleRate][max[3] + 1];
    } else {
      static const int pretab1[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0};
      static const int pretab2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

      auto *pretab = (const int *)(granule.preflag ? pretab1 : pretab2);
      int max = -1, cb = 0, mc = 0;
      auto *map = constants.map[sampleRate][2];
      float v = 0;

      for (int i = 0; i < 3; ++i) {
        auto *h = huffmanTables1 + granule.tableSelect[i];

        for (int lp = l[i]; lp != 0; --lp, --mc) {
          if (mc == 0) {
            mc = *map++;
            v = granule.pow2gain[((*scf++) + (*pretab++)) << shift];
            cb = *map++;
          }

          auto *val = h->table;
          int y;

          while ((y = *val++) < 0) {
            if (getOneBit())
              val -= y;
            --part2remain;
          }

          int x = y >> 4;
          y &= 15;

          if (x == 15) {
            max = cb;
            part2remain -= (int)(h->bits + 1);
            x += (int)getBits((int)h->bits);
            *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v);
          } else if (x) {
            max = cb;
            *xrpnt++ = constants.nToThe4Over3[x] * (getOneBit() ? -v : v);
            --part2remain;
          } else
            *xrpnt++ = 0;

          if (y == 15) {
            max = cb;
            part2remain -= (int)(h->bits + 1);
            y += (int)getBits((int)h->bits);
            *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v);
          } else if (y) {
            max = cb;
            *xrpnt++ = constants.nToThe4Over3[y] * (getOneBit() ? -v : v);
            --part2remain;
          } else
            *xrpnt++ = 0;
        }
      }

      for (; l3 && part2remain > 0; --l3) {
        auto *h = huffmanTables2 + granule.count1TableSelect;
        auto *values = h->table;
        int16 a;

        while ((a = *values++) < 0) {
          if (part2remain <= 0) {
            a = 0;
            break;
          }

          --part2remain;

          if (getOneBit())
            values -= a;
        }

        for (int i = 0; i < 4; ++i) {
          if ((i & 1) == 0) {
            if (mc == 0) {
              mc = *map++;
              cb = *map++;
              v = granule.pow2gain[((*scf++) + (*pretab++)) << shift];
            }
            --mc;
          }

          if ((a & (0x8 >> i))) {
            max = cb;

            if (part2remain <= 0)
              break;

            --part2remain;
            *xrpnt++ = getOneBit() ? -v : v;
          } else
            *xrpnt++ = 0;
        }
      }

      zeromem(xrpnt, (size_t)(&xr[32][0] - xrpnt) * sizeof(float));

      granule.maxBandl = (uint32)(max + 1);
      granule.maxb = (uint32)constants.longLimit[sampleRate][granule.maxBandl];
    }

    while (part2remain > 16) {
      getBits(16);
      part2remain -= 16;
    }

    if (part2remain > 0)
      getBits(part2remain);
    else if (part2remain < 0)
      return true;

    return false;
  }

  void layer3Hybrid(float fsIn[32][18], float tsOut[18][32], int ch,
                    const Layer3SideInfo::Info &granule) noexcept {
    auto *ts = (float *)tsOut;
    float *rawout1, *rawout2;
    int sb = 0;

    {
      int b = hybridBlockIndex[ch];
      rawout1 = hybridBlock[b][ch];
      b = 1 - b;
      rawout2 = hybridBlock[b][ch];
      hybridBlockIndex[ch] = b;
    }

    if (granule.mixedBlockFlag) {
      sb = 2;
      DCT::dct36(fsIn[0], rawout1, rawout2, constants.win[0], ts);
      DCT::dct36(fsIn[1], rawout1 + 18, rawout2 + 18, constants.win1[0],
                 ts + 1);
      rawout1 += 36;
      rawout2 += 36;
      ts += 2;
    }

    auto bt = granule.blockType;

    if (bt == 2) {
      for (; sb < (int)granule.maxb;
           sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) {
        DCT::dct12(fsIn[sb], rawout1, rawout2, constants.win[2], ts);
        DCT::dct12(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, constants.win1[2],
                   ts + 1);
      }
    } else {
      for (; sb < (int)granule.maxb;
           sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) {
        DCT::dct36(fsIn[sb], rawout1, rawout2, constants.win[bt], ts);
        DCT::dct36(fsIn[sb + 1], rawout1 + 18, rawout2 + 18, constants.win1[bt],
                   ts + 1);
      }
    }

    for (; sb < 32; ++sb, ++ts) {
      for (int i = 0; i < 18; ++i) {
        ts[i * 32] = *rawout1++;
        *rawout2++ = 0;
      }
    }
  }

  void synthesiseStereo(const float *bandPtr0, const float *bandPtr1,
                        float *out0, float *out1, int &samplesDone) noexcept {
    auto dummy = samplesDone;
    synthesise(bandPtr0, 0, out0, dummy);
    synthesise(bandPtr1, 1, out1, samplesDone);
  }

  void synthesise(const float *bandPtr, int channel, float *out,
                  int &samplesDone) {
    out += samplesDone;
    const int bo = channel == 0 ? ((synthBo - 1) & 15) : synthBo;
    float(*buf)[0x110] = synthBuffers[channel];
    float *b0;
    auto bo1 = bo;

    if (bo & 1) {
      b0 = buf[0];
      DCT::dct64(buf[1] + ((bo + 1) & 15), buf[0] + bo, bandPtr);
    } else {
      ++bo1;
      b0 = buf[1];
      DCT::dct64(buf[0] + bo, buf[1] + bo1, bandPtr);
    }

    synthBo = bo;
    const float *window = constants.decodeWin + 16 - bo1;

    for (int j = 16; j != 0; --j, b0 += 16, window += 32) {
      auto sum = window[0] * b0[0];
      sum -= window[1] * b0[1];
      sum += window[2] * b0[2];
      sum -= window[3] * b0[3];
      sum += window[4] * b0[4];
      sum -= window[5] * b0[5];
      sum += window[6] * b0[6];
      sum -= window[7] * b0[7];
      sum += window[8] * b0[8];
      sum -= window[9] * b0[9];
      sum += window[10] * b0[10];
      sum -= window[11] * b0[11];
      sum += window[12] * b0[12];
      sum -= window[13] * b0[13];
      sum += window[14] * b0[14];
      sum -= window[15] * b0[15];
      *out++ = sum;
    }

    {
      auto sum = window[0] * b0[0];
      sum += window[2] * b0[2];
      sum += window[4] * b0[4];
      sum += window[6] * b0[6];
      sum += window[8] * b0[8];
      sum += window[10] * b0[10];
      sum += window[12] * b0[12];
      sum += window[14] * b0[14];
      *out++ = sum;
      b0 -= 16;
      window -= 32;
      window += (ptrdiff_t)bo1 << 1;
    }

    for (int j = 15; j != 0; --j, b0 -= 16, window -= 32) {
      auto sum = -window[-1] * b0[0];
      sum -= window[-2] * b0[1];
      sum -= window[-3] * b0[2];
      sum -= window[-4] * b0[3];
      sum -= window[-5] * b0[4];
      sum -= window[-6] * b0[5];
      sum -= window[-7] * b0[6];
      sum -= window[-8] * b0[7];
      sum -= window[-9] * b0[8];
      sum -= window[-10] * b0[9];
      sum -= window[-11] * b0[10];
      sum -= window[-12] * b0[11];
      sum -= window[-13] * b0[12];
      sum -= window[-14] * b0[13];
      sum -= window[-15] * b0[14];
      sum -= window[0] * b0[15];
      *out++ = sum;
    }

    samplesDone += 32;
  }

  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PatchedMP3Stream)
};

//==============================================================================
static const char *const mp3FormatName = "MP3 file";

//==============================================================================
class PatchedMP3Reader : public AudioFormatReaderWithPosition {
public:
  PatchedMP3Reader(InputStream *const in)
      : AudioFormatReaderWithPosition(in, mp3FormatName), stream(*in),
        currentPosition(0), decodedStart(0), decodedEnd(0) {
    skipID3();
    const int64 streamPos = stream.stream.getPosition();

    if (readNextBlock()) {
      bitsPerSample = 32;
      usesFloatingPointData = true;
      sampleRate = stream.frame.getFrequency();
      numChannels = (unsigned int)stream.frame.numChannels;
      samplesPerFrame = stream.frame.numSamples();
      lengthInSamples = findLength(streamPos);
    }
  }

  bool readSamples(int **destSamples, int numDestChannels,
                   int startOffsetInDestBuffer, int64 startSampleInFile,
                   int numSamples) override {
    if (destSamples == nullptr) {
      jassertfalse;
      return false;
    }

    if (numDestChannels == 0) {
      return true;
    }

    if (currentPosition != startSampleInFile) {
      if (!stream.seek((int)(startSampleInFile / samplesPerFrame - 1))) {
        currentPosition = -1;
        createEmptyDecodedData();
      } else {
        decodedStart = decodedEnd = 0;
        const int64 streamPos = stream.currentFrameIndex * samplesPerFrame;
        int toSkip = (int)(startSampleInFile - streamPos);
        jassert(toSkip >= 0);

        while (toSkip > 0) {
          if (!readNextBlock()) {
            createEmptyDecodedData();
            break;
          }

          const int numReady = decodedEnd - decodedStart;

          if (numReady > toSkip) {
            decodedStart += toSkip;
            break;
          }

          toSkip -= numReady;
        }

        currentPosition = startSampleInFile;
      }
    }

    while (numSamples > 0) {
      if (decodedEnd <= decodedStart && !readNextBlock()) {
        for (int i = numDestChannels; --i >= 0;)
          if (destSamples[i] != nullptr)
            zeromem(destSamples[i] + startOffsetInDestBuffer,
                    (size_t)numSamples * sizeof(float));

        return false;
      }

      const int numToCopy = jmin(decodedEnd - decodedStart, numSamples);
      float *const *const dst = reinterpret_cast<float **>(destSamples);
      memcpy(dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart,
             (size_t)numToCopy * sizeof(float));

      if (numDestChannels > 1 && dst[1] != nullptr)
        memcpy(dst[1] + startOffsetInDestBuffer,
               (numChannels < 2 ? decoded0 : decoded1) + decodedStart,
               (size_t)numToCopy * sizeof(float));

      startOffsetInDestBuffer += numToCopy;
      decodedStart += numToCopy;
      currentPosition += numToCopy;
      numSamples -= numToCopy;
    }

    return true;
  }

  int64 getCurrentPosition() const override { return currentPosition; }

  bool lengthIsApproximate() const override {
    // stream.numFrames will only be set if we have a VBR header,
    // which identifies the exact number of samples expected in the stream:
    return stream.numFrames <= 0;
  }

private:
  PatchedMP3Stream stream;
  int64 currentPosition;
  int samplesPerFrame;
  enum { decodedDataSize = 1152 };
  float decoded0[decodedDataSize], decoded1[decodedDataSize];
  int decodedStart, decodedEnd;

  void createEmptyDecodedData() noexcept {
    zeromem(decoded0, sizeof(decoded0));
    zeromem(decoded1, sizeof(decoded1));
    decodedStart = 0;
    decodedEnd = decodedDataSize;
  }

  bool readNextBlock() {
    for (int attempts = 10; --attempts >= 0;) {
      int samplesDone = 0;

      if (stream.stream.isExhausted()) {
        createEmptyDecodedData();
        return true;
      }

      const int result =
          stream.decodeNextBlock(decoded0, decoded1, samplesDone);

      if (result > 0 && stream.stream.isExhausted()) {
        createEmptyDecodedData();
        return true;
      }

      if (result <= 0) {
        decodedStart = 0;
        decodedEnd = samplesDone;
        return result == 0;
      }
    }

    return false;
  }

  void skipID3() {
    const int64 originalPosition = stream.stream.getPosition();
    const uint32 firstWord = (uint32)stream.stream.readInt();

    if ((firstWord & 0xffffff) == 0x334449) {
      uint8 buffer[6];

      if (stream.stream.read(buffer, 6) == 6 && buffer[0] != 0xff &&
          ((buffer[2] | buffer[3] | buffer[4] | buffer[5]) & 0x80) == 0) {
        const uint32 length = (((uint32)buffer[2]) << 21) |
                              (((uint32)buffer[3]) << 14) |
                              (((uint32)buffer[4]) << 7) | ((uint32)buffer[5]);

        stream.stream.skipNextBytes(length);
        return;
      }
    }

    stream.stream.setPosition(originalPosition);
  }

  int64 findLength(int64 streamStartPos) {
    int64 numFrames = stream.numFrames;

    if (numFrames <= 0) {
      const int64 streamSize = stream.stream.getTotalLength();

      if (streamSize > 0) {
        const int bytesPerFrame = stream.frame.frameSize + 4;

        if (bytesPerFrame == 417 || bytesPerFrame == 418)
          numFrames = roundToInt((double)(streamSize - streamStartPos) /
                                 417.95918); // more accurate for 128k
        else
          numFrames = (streamSize - streamStartPos) / bytesPerFrame;
      }
    }

    return numFrames * samplesPerFrame;
  }

  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PatchedMP3Reader)
};

} // namespace PatchedMP3Decoder

//==============================================================================
PatchedMP3AudioFormat::PatchedMP3AudioFormat()
    : AudioFormat(PatchedMP3Decoder::mp3FormatName, ".mp3") {}
PatchedMP3AudioFormat::~PatchedMP3AudioFormat() {}

Array<int> PatchedMP3AudioFormat::getPossibleSampleRates() { return {}; }
Array<int> PatchedMP3AudioFormat::getPossibleBitDepths() { return {}; }
bool PatchedMP3AudioFormat::canDoStereo() { return true; }
bool PatchedMP3AudioFormat::canDoMono() { return true; }
bool PatchedMP3AudioFormat::isCompressed() { return true; }
StringArray PatchedMP3AudioFormat::getQualityOptions() { return {}; }

AudioFormatReader *
PatchedMP3AudioFormat::createReaderFor(InputStream *sourceStream,
                                       const bool deleteStreamIfOpeningFails) {
  std::unique_ptr<PatchedMP3Decoder::PatchedMP3Reader> r(
      new PatchedMP3Decoder::PatchedMP3Reader(sourceStream));

  if (r->lengthInSamples > 0)
    return r.release();

  if (!deleteStreamIfOpeningFails)
    r->input = nullptr;

  return nullptr;
}

AudioFormatWriter *PatchedMP3AudioFormat::createWriterFor(
    OutputStream *, double /*sampleRateToUse*/,
    unsigned int /*numberOfChannels*/, int /*bitsPerSample*/,
    const StringPairArray & /*metadataValues*/, int /*qualityOptionIndex*/) {
  jassertfalse; // not yet implemented!
  return nullptr;
}

} // namespace juce
